「Rustの所有権やライフタイムが難しすぎる」——これはRust学習者の90%以上が口にする悩みです。Gemini CLI(Google Antigravity)を活用すれば、コンパイラエラーの解読からイディオマティックなRustコードの生成まで、学習と開発の両方を大幅に効率化できます。
Gemini CLIはRustの所有権システム・ライフタイム・トレイトシステムを深く理解しており、コンパイラエラーの解説から最適なパターンの提案まで、Rust開発者の強力なパートナーになります。この記事では、Google Antigravityシリーズ第43弾として、Rust開発の効率化テクニックを実践的に解説します。
この記事を3秒でまとめると
- Gemini CLIでRustの所有権・ライフタイムエラーを即座に解読し、修正案を得られる
- トレイト設計・エラーハンドリング・並行処理パターンをGemini CLIで効率的に実装
- SES現場で需要の高いRustシステム開発(WebAssembly・組み込み・CLIツール)をカバー
なぜGemini CLI × Rust開発なのか
Rust言語のSES市場での急成長
2026年、Rust言語のSES市場での需要は過去最高を記録しています。
| 指標 | 数値 |
|---|---|
| Rust関連SES案件の前年比増加率 | +52% |
| Rust案件の平均単価(月額) | 80〜110万円 |
| 主な採用分野 | インフラ・ブロックチェーン・組み込み・WebAssembly |
| Stack Overflow人気言語ランキング | 8年連続「最も愛されている言語」1位 |
Rustの需要が急増している理由は、メモリ安全性をコンパイル時に保証するという他言語にない特性にあります。C/C++の代替として、セキュリティクリティカルなシステム開発で採用が進んでいます。
Gemini CLIがRust開発に最適な理由
1. Geminiモデルの長文コンテキスト Gemini CLIは100万トークン以上のコンテキストウィンドウを持つため、大規模なRustプロジェクト全体を一度に分析できます。
2. マルチモーダル対応 コンパイラエラーのスクリーンショットを渡して「これを修正して」という使い方も可能です。
3. Google検索との統合 Rustの最新のベストプラクティスやクレート情報をリアルタイムで検索し、最新の情報に基づいた提案ができます。
Rust開発環境のGemini CLI設定
GEMINI.mdの推奨設定
Rustプロジェクト用のGEMINI.md(プロジェクトコンテキストファイル)を設定します。
# Rust Project Guidelines
## Edition & Toolchain
- Rust 2024 edition
- Minimum Rust version: 1.78
- Use cargo clippy with pedantic warnings
## Style & Patterns
- Follow Rust API Guidelines (https://rust-lang.github.io/api-guidelines/)
- Prefer &str over String for function parameters
- Use thiserror for library errors, anyhow for applications
- Prefer iterators over explicit loops
- No unwrap() in production code (use expect() with message or ? operator)
## Architecture
- Module structure follows domain boundaries
- Public API is minimal (pub(crate) by default)
- Derive macros: Debug, Clone, PartialEq at minimum
## Testing
- Unit tests in same file (#[cfg(test)] mod tests)
- Integration tests in tests/ directory
- Property-based tests with proptest for critical logic
初期プロジェクト構成の生成
gemini "以下の仕様でRustプロジェクトを初期化してください:
- CLIツール: ログファイル分析ツール
- clap v4 でCLI引数パーシング
- tokio でAsync I/O
- serde + serde_json でデータ処理
- tracing でログ出力
- thiserror でエラー定義
- Cargo.toml にworkspace設定(将来のクレート分割に備える)"
所有権・借用・ライフタイムの攻略
コンパイラエラーの解読
Rustで最も多い挫折ポイントは、所有権関連のコンパイラエラーです。Gemini CLIに解説させます。
gemini "以下のRustコンパイラエラーを解説し、修正案を3つ提示してください。
それぞれの修正案のメリット・デメリットも説明してください。
error[E0502]: cannot borrow \`data\` as mutable because it is also borrowed as immutable
--> src/main.rs:10:5
|
8 | let ref1 = &data[0];
| ---- immutable borrow occurs here
9 |
10 | data.push(42);
| ^^^^ mutable borrow occurs here
11 |
12 | println!(\"{}\", ref1);
| ---- immutable borrow later used here"
Gemini CLIの回答例:
## エラー解説
Rustの借用ルール違反です。`data`の不変借用(ref1)が生きている間に、
可変借用(push)を行おうとしています。
## 修正案
### 案1: 借用のスコープを調整(推奨)
不変借用の使用を、可変借用の前に終わらせます。
```rust
let data_val = data[0]; // コピーして借用を解放
data.push(42);
println!("{}", data_val);
メリット: シンプル、ゼロコスト デメリット: 値がCopyトレイトを実装している場合のみ使用可能
案2: clone()で独立したコピーを作成
let ref1 = data[0].clone();
data.push(42);
println!("{}", ref1);
メリット: 任意の型で使える デメリット: ヒープアロケーションが発生する可能性
案3: インデックスで後からアクセス
let idx = 0;
data.push(42);
println!("{}", data[idx]);
メリット: アロケーション不要 デメリット: push後にインデックスが無効になる可能性に注意
### ライフタイム設計のベストプラクティス
```bash
gemini "以下の構造体にライフタイムパラメータを適切に設定してください。
参照を持つ理由と、所有権を持たせるべき場合の判断基準も説明してください。
struct Config {
name: &str,
database_url: &str,
allowed_origins: Vec<&str>,
}
struct RequestContext {
config: &Config,
user_id: &str,
trace_id: String,
}"
// ライフタイム付きの正しい定義
struct Config<'a> {
name: &'a str,
database_url: &'a str,
allowed_origins: Vec<&'a str>,
}
struct RequestContext<'a, 'b> {
config: &'b Config<'a>,
user_id: &'b str,
trace_id: String, // 所有権を持つ(リクエストごとに生成されるため)
}
// しかし、多くの場合は所有権を持たせる方がシンプル
struct ConfigOwned {
name: String,
database_url: String,
allowed_origins: Vec<String>,
}
Gemini CLIのアドバイス:
- ライフタイムが3つ以上になったら、設計を見直すサイン
- 長期間生存する構造体には
String(所有権あり)を使う - 関数パラメータには
&str(借用)を使う

トレイト設計とジェネリクス
トレイトの設計パターン
Rustのトレイトは、Goのインターフェースに相当する抽象化機構です。
gemini "以下の要件でリポジトリパターンをトレイトで設計してください:
1. async対応(async_trait使用)
2. ジェネリクスで型安全
3. テスト用のモック実装も生成
4. エラー型はthiserrorで定義"
use async_trait::async_trait;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum RepositoryError {
#[error("not found: {0}")]
NotFound(String),
#[error("duplicate: {0}")]
Duplicate(String),
#[error("database error: {source}")]
Database {
#[from]
source: sqlx::Error,
},
}
#[async_trait]
pub trait Repository<T>: Send + Sync {
async fn find_by_id(&self, id: &str) -> Result<T, RepositoryError>;
async fn find_all(&self, limit: usize, offset: usize) -> Result<Vec<T>, RepositoryError>;
async fn create(&self, entity: &T) -> Result<T, RepositoryError>;
async fn update(&self, entity: &T) -> Result<T, RepositoryError>;
async fn delete(&self, id: &str) -> Result<(), RepositoryError>;
}
// テスト用のモック実装
#[cfg(test)]
pub struct MockRepository<T: Clone + Send + Sync> {
pub items: std::sync::Mutex<Vec<T>>,
}
型状態パターン(Type State Pattern)
Rustならではの強力なパターンをGemini CLIで実装します。
gemini "HTTPリクエストビルダーを型状態パターンで実装してください。
コンパイル時にURLが設定されていないリクエストのsend()を防止する設計にしてください。"
// 型状態を表すマーカー型
struct NoUrl;
struct HasUrl;
struct RequestBuilder<U> {
url: Option<String>,
headers: Vec<(String, String)>,
body: Option<String>,
_state: std::marker::PhantomData<U>,
}
impl RequestBuilder<NoUrl> {
fn new() -> Self {
RequestBuilder {
url: None,
headers: Vec::new(),
body: None,
_state: std::marker::PhantomData,
}
}
fn url(self, url: impl Into<String>) -> RequestBuilder<HasUrl> {
RequestBuilder {
url: Some(url.into()),
headers: self.headers,
body: self.body,
_state: std::marker::PhantomData,
}
}
}
impl RequestBuilder<HasUrl> {
// send()はUrlが設定済みの場合のみ呼べる
async fn send(self) -> Result<Response, Error> {
// URLは必ずSome(型で保証済み)
let url = self.url.unwrap();
// ...
}
}
// コンパイルエラー: send()はNoUrl状態では呼べない
// RequestBuilder::new().send(); // ← コンパイルエラー!
// OK: url()を呼んでからsend()
// RequestBuilder::new().url("https://api.example.com").send();
エラーハンドリングの設計
thiserror と anyhow の使い分け
gemini "このプロジェクトのエラーハンドリング戦略を設計してください。
以下の判断基準に基づいてthiserrorとanyhowを使い分けてください:
- ライブラリクレート → thiserror(呼び出し元がエラーをマッチングできるように)
- アプリケーション(バイナリ)→ anyhow(コンテキスト追加が簡単)
- ドメインレイヤー → thiserror
- ハンドラーレイヤー → anyhow + HTTP変換"
// domain/error.rs - thiserror使用
#[derive(Error, Debug)]
pub enum DomainError {
#[error("user not found: {user_id}")]
UserNotFound { user_id: String },
#[error("email already registered: {email}")]
EmailDuplicate { email: String },
#[error("validation failed: {0}")]
Validation(String),
}
// handler/error.rs - anyhow + HTTP変換
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match self.0.downcast_ref::<DomainError>() {
Some(DomainError::UserNotFound { .. }) => {
(StatusCode::NOT_FOUND, self.0.to_string())
}
Some(DomainError::EmailDuplicate { .. }) => {
(StatusCode::CONFLICT, self.0.to_string())
}
Some(DomainError::Validation(msg)) => {
(StatusCode::BAD_REQUEST, msg.clone())
}
None => {
tracing::error!("Internal error: {:?}", self.0);
(StatusCode::INTERNAL_SERVER_ERROR, "内部エラー".to_string())
}
};
(status, Json(json!({ "error": message }))).into_response()
}
}
並行処理とasyncプログラミング
Tokioランタイムの活用
gemini "以下の要件で非同期HTTPクライアントを実装してください:
1. reqwest + tokio
2. レート制限(1秒あたり10リクエスト)
3. リトライ(指数バックオフ、最大3回)
4. タイムアウト(リクエスト30秒、接続5秒)
5. コネクションプール管理
6. テスト用のモックサーバー(wiremock使用)"
チャネルを使った並行パイプライン
use tokio::sync::mpsc;
async fn process_pipeline(input: Vec<RawData>) -> Result<Vec<ProcessedData>> {
let (tx1, mut rx1) = mpsc::channel::<ValidatedData>(100);
let (tx2, mut rx2) = mpsc::channel::<ProcessedData>(100);
// Stage 1: バリデーション
let validator = tokio::spawn(async move {
for item in input {
if let Ok(validated) = validate(item).await {
tx1.send(validated).await.ok();
}
}
});
// Stage 2: データ変換
let transformer = tokio::spawn(async move {
while let Some(item) = rx1.recv().await {
let processed = transform(item).await;
tx2.send(processed).await.ok();
}
});
// Stage 3: 結果収集
let mut results = Vec::new();
while let Some(item) = rx2.recv().await {
results.push(item);
}
validator.await?;
transformer.await?;
Ok(results)
}
SES現場でのRust活用パターン
パターン1: WebAssemblyモジュール開発
gemini "Rust + wasm-bindgenで、画像処理用のWebAssemblyモジュールを生成してください。
以下の関数を実装:
- resize(width, height) - 画像リサイズ
- crop(x, y, w, h) - 画像切り抜き
- grayscale() - グレースケール変換
JavaScript側のAPIも生成してください。"
パターン2: 高性能CLIツール開発
gemini "Rustで高性能なログ分析CLIツールを生成してください。
以下の機能:
- 大容量ファイル(数GB)のストリーミング処理
- 正規表現フィルタリング
- JSON/CSV出力
- プログレスバー表示
- 並列処理(rayon使用)"
パターン3: マイクロサービスのパフォーマンスクリティカル部分
gemini "Axumフレームワークで高性能REST APIサーバーを構築してください。
要件:
- 1万req/sec以上のスループット
- P99レイテンシ10ms以下
- PostgreSQL(sqlx)+ Redis(deadpool-redis)
- OpenTelemetryトレーシング
- Docker + Kubernetesデプロイ対応"
マイクロサービスガイドで、アーキテクチャ設計の詳細を解説しています。
テスト戦略
プロパティベーステスト
gemini "proptest を使って、以下の関数のプロパティベーステストを生成してください:
- parse_email(): 任意の文字列に対してパニックしないこと
- serialize/deserialize: 往復変換で元のデータと一致すること
- sort_users(): 出力が常にソート済みであること"
use proptest::prelude::*;
proptest! {
#[test]
fn parse_email_never_panics(s in "\\PC*") {
let _ = parse_email(&s); // パニックしなければOK
}
#[test]
fn roundtrip_serialization(user in arb_user()) {
let json = serde_json::to_string(&user).unwrap();
let deserialized: User = serde_json::from_str(&json).unwrap();
prop_assert_eq!(user, deserialized);
}
}
テスト自動化ガイドでも、テスト戦略の基礎を解説しています。
まとめ:Gemini CLI × RustでSES市場の高単価案件を獲得する
Gemini CLIを活用したRust開発により、学習の壁を大幅に下げながら、高品質なRustコードを書けるようになります。
Rust開発でGemini CLIを活用するステップ:
- GEMINI.mdにプロジェクトルールを設定し、一貫性のあるコード生成を実現
- コンパイラエラーの解読をGemini CLIに任せ、学習を加速
- トレイト設計・エラーハンドリングの方針をGemini CLIと策定
- テスト自動生成で品質を担保しながら開発速度を維持
- プロファイリング結果の分析で最適化を継続
**Rust案件の平均単価は80〜110万円と高水準です。**Gemini CLIを活用して、Rustスキルを効率的に習得し、高単価案件を獲得しましょう。
SES BASEでは、Rust・システムプログラミング領域のSES案件も掲載しています。案件を検索するからチェックしてみてください。