RustはAI支援が最も派手に失敗しやすい言語だ。所有権システム・借用チェッカー・厳格な型保証のせいで、一見もっともらしいAI生成コードがことごとくコンパイルエラーになる。CLAUDE.mdで明示的なガイダンスを与えなければ、Claude Codeは他の言語から学んだパターンにフォールバックする。そのパターンはRustには通用しない。
実際にCLAUDE.md未設定のまま動かすと、こういうことが起きる:
- ライブラリコードに
.unwrap()が散乱する Arc<Mutex<T>>とRc<RefCell<T>>が同じ非同期コンテキストに混在するunsafeブロックがSafetyコメントなしで追加される- ClippyのwarningがCI前に残り続ける
- 既存の
thiserror定義を使わず、その場でエラー型を新定義する
CLAUDE.mdを正しく設定するとこれらが全部解消される。何を書けばいいかを具体的に説明する。
なぜRust + Claude Codeにはコンフィグが必要か
Claude CodeはRustを理解している。ライフタイム推論、トレイト設計、借用チェッカーのエラー修正——これらは明確な時間節約になる。問題は理解力ではなく、デフォルト動作だ。
デフォルトのClaude Codeは「コンパイルが通ってテストが通るコード」に最適化されている。Rustではこれは思ったより低い基準だ。ライブラリ中に .unwrap() が散らばっていてもテストは通る。unsafe がドキュメントなしでも動く。
CLAUDE.mdは最適化ターゲットを変える。「コンパイルが通るコード」から「このプロジェクトの規約に沿ったコード」へ。これがそのままレビューで差し戻すコードと出荷できるコードの差になる。
完全なCLAUDE.mdテンプレート
新しいRustプロジェクトで使っている出発点のテンプレートを示す。コピーして、不要なセクションを削り、プロジェクト固有の注釈を追加する。
# Rust Project: [ProjectName]
## Build & Test Commands
- Build: `cargo build`
- Test: `cargo test`
- Test with output: `cargo test -- --nocapture`
- Lint: `cargo clippy -- -D warnings`
- Format: `cargo fmt`
- Check (fast): `cargo check`
- Audit: `cargo audit`
## Rust Edition & Toolchain
- Edition: 2021(Cargo.tomlに必ず明記)
- MSRV: 1.75.0(stable、rust-toolchain.tomlで固定)
- nightly機能は使用禁止
## Error Handling
- ライブラリ: `thiserror` — 全カスタムエラーに `Error` derive
- バイナリ/アプリ: `anyhow` — `?` で伝播、`.context()` でコンテキスト付与
- ライブラリコードに `.unwrap()` や `.expect()` を使わない
- `.expect()` はバイナリの `main()` のセットアップ失敗にのみ許容
- テストコードでも `.unwrap()` 禁止 — `?` で伝播する
- エラーは `?` で伝播する。ローカル処理に理由があるなら必ずコメントを残す
## Clippy Policy
- 全warningをエラーとして扱う: `cargo clippy -- -D warnings`
- コミット前にClippyを必ず実行する
- `#[allow(clippy::...)]` を使う場合は理由コメントが必須
## コードスタイル
- 全publicアイテム(struct/enum/fn/trait/mod)に `///` docコメント必須
- privateアイテムのdocコメントは推奨
- publicな関数シグネチャには明示的な型アノテーションを付ける
- ワイルドカードインポート(`use foo::*`)はtestモジュール以外禁止
## Dependency Policy
- 依存は最小限に。新規クレートはPR説明に正当性を書く
- バイナリ: バージョンを固定(`=1.2.3`)
- ライブラリ: `^`(キャレット)で互換バージョン許容
- リリース前に `cargo audit` を必ず実行
## Memory Safety & Unsafe
- `unsafe` ブロックは `// SAFETY:` コメントで不変条件を必ず説明する
- unsafeが必要な場合は専用モジュール(`src/ffi/` 等)に分離する
- スレッド間共有状態には `Arc<Mutex<T>>` を使う(生ポインタ禁止)
- 非同期コンテキストで `Rc<RefCell<T>>` を使わない(`Send`でない)
- unsafeを書く前に必ず相談する — 安全な代替案があるかもしれない
ビルド&テストコマンドの設定
コマンドセクションは即効性が高い。Claude Codeはターミナルコマンドを実行する前にここを読む。これにより「本当は高速チェックしたいのに cargo build --release が走る」「Clippyが完全にスキップされる」という典型的な失敗を防ぐ。
cargo check と cargo build の使い分けも明記しておくと効果的だ。cargo check はコード生成を行わずに型チェックと借用チェックのみを実行するため、一般に3〜5倍速い。Claude Codeに「探索中は cargo check、最終確認には cargo build」と指示しておく。
エラーハンドリング規則
エラーハンドリングはRustプロジェクトのCLAUDE.mdで最もレバレッジが効くセクションだ。thiserror / anyhow の使い分けは確立した規約だが、指示がなければClaude Codeは適用しない。
基本的な考え方:ライブラリは thiserror(呼び出し元がエラーバリアントにマッチする必要があるため)、バイナリは anyhow(ユーザーに表示できるエラーがあれば十分なため)。これを明文化する:
## Error Handling(ライブラリクレートの場合)
- 全エラー型は `src/error.rs` に定義する
- `thiserror::Error` deriveマクロを使う
- エラーバリアントには `#[error("...")]` で説明的なメッセージを付ける
- 外部クレートのエラーは `#[from]` でラップする
例:
```rust
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("設定キーが見つかりません: {key}")]
KeyNotFound { key: String },
#[error("IO エラー: {0}")]
Io(#[from] std::io::Error),
}
## Memory Safetyポリシー
`unsafe` ブロックのポリシーは独立したセクションを設ける価値がある。Claude Codeはsoundな `unsafe` Rustを書けるが、自動的には安全性の根拠をドキュメント化しない。
`// SAFETY:` コメントの要件はCLAUDE.mdで最も重要なルールの一つだ。これを設定すると、Claude Codeはunsafeブロックが健全な理由を明示するよう強制される:
```markdown
## Memory Safety
- 全unsafeブロックに `// SAFETY:` コメントが必須
- コメントには(1)維持している不変条件、(2)コードが健全な理由を書く
- unsafeは `src/ffi.rs` または `src/sys.rs` に分離する — ビジネスロジックに直接書かない
許容されるunsafeの例:
```rust
// SAFETY: `ptr` はこのブロックの期間中、排他的アクセスを保持している。
// Box::into_raw から取得したポインタであり、エイリアシングされていない。
let value = unsafe { Box::from_raw(ptr) };
## rust-analyzer との連携
Claude Codeとrust-analyzerは並行して動作する。rust-analyzerはLSP——エディタにリアルタイム診断・定義ジャンプ・補完を提供する。Claude Codeは `cargo check` や `cargo clippy` のターミナル出力を読む。直接LSPプロトコルで通信するわけではなく、補完関係だ。
実践的な使い方:rust-analyzerのリアルタイムフィードバックで探索し、コンパイラエラーを修正させたいときは `cargo check` の出力をClaude Codeに貼り付ける:
$ cargo check 2>&1 | head -30
error[E0502]: cannot borrow data as mutable because it is also borrowed as immutable
—> src/main.rs:15:5
この出力を「このエラーを修正して」と添えてClaude Codeに渡すと、自然言語でエラーを説明するよりはるかに精度が高い修正が返ってくる。
CLAUDE.mdにrust-analyzerについても記述しておく:
```markdown
## LSP & IDE
- LSP: rust-analyzer(.vscode/settings.json またはエディタの設定で有効化)
- rust-analyzerとcargo checkの診断は実質同等
- Claude Codeにエラーを修正させる場合: `cargo check 2>&1` の出力をそのまま貼る
Hooksによる自動化
Claude Codeの PostToolUse フックを使うと、ファイル編集のたびに cargo check を自動実行してClaude Code自身にフィードバックを返せる。.claude/settings.json に追加:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cargo check 2>&1 | tail -20"
}
]
}
]
}
}
このフックにより、Claude Codeは編集のたびにコンパイラの診断を受け取る。借用チェッカーエラーを引き起こしても、次のツール呼び出しで自己修正できる。手動で cargo check を実行して出力を貼り付ける作業が不要になる。
Clippyで強化したバージョン:
{
"command": "cargo clippy -- -D warnings 2>&1 | tail -30"
}
checkより遅いが、正確性と同時にスタイル問題も同一ループでフィードバックできる。
非同期Rust(tokio)の設定
tokioを使う場合、ランタイムの選択と非同期パターンをCLAUDE.mdに明記する。指示がないとClaude Codeはtokioとasync-std両方で動くパターンを書こうとして、結果的にどちらでも微調整が必要なコードが生成されることがある:
## Async Runtime: tokio
- エントリポイント: `#[tokio::main]`
- テスト: `#[tokio::test]`
- 並行タスク: `tokio::spawn`(Sendバウンドが必要)
- 複数futureの競合: `tokio::select!`(ネストしたawaitチェーンは使わない)
- 非同期コンテキストのMutex: `tokio::sync::Mutex`(`std::sync::Mutex`は不可)
- CPUバウンド処理: `tokio::task::spawn_blocking` でラップする
- tokioとasync-stdを同一バイナリで混在させない
クロスツール互換 AGENTS.md テンプレート
Cursor・Copilot Workspace・CodexなどClaude Code以外のエージェントも使うチームには、プロジェクトルートにAGENTS.mdを置く。CLAUDE.mdと同じ書き方で全ツールが読める:
# AGENTS.md — [ProjectName]
## Build Commands
- `cargo check` — 高速型チェック
- `cargo clippy -- -D warnings` — lint(warningをエラー扱い)
- `cargo test` — 全テスト実行
- `cargo fmt` — フォーマット(コミット前必須)
- `cargo audit` — 依存パッケージのセキュリティ監査
## コード規約
- Rust edition 2021
- エラーハンドリング: ライブラリ=`thiserror`、バイナリ=`anyhow`
- ライブラリコードで `.unwrap()` 禁止 — `?` で伝播
- 全unsafeブロックに `// SAFETY:` コメント必須
- 全publicアイテムに `///` docコメント必須
## やってはいけないこと
- ライブラリコードに `.unwrap()` を追加する
- `// SAFETY:` コメントなしで `unsafe` を書く
- `src/error.rs` 以外でエラー型を新定義する
- 依存追加の正当性をPRに書かずにクレートを追加する
よくある間違いを避ける
実際のRustコードベースにAIエージェントを走らせて観察した、CLAUDE.md未設定時の典型的なアンチパターン:
unwrapの蔓延。 コンパイルを通らせる最も手っ取り早い方法は .unwrap() を追加することだ。テストは通るが、本番でエッジケースに当たるとpanicになる。
スレッド安全性の混乱。 Rc<RefCell<T>>(Sendでない)と tokio::spawn(Sendが必要)の組み合わせはコンパイルエラーだが、エージェントは一般的なパターンとして生成する。非同期共有状態には Arc<Mutex<T>> を指定する。
Safetyコメントなしのunsafe。 Claude CodeはsoundなunsafeなRustを書ける。問題はそれがなぜsoundなのかを自動でドキュメント化しないことだ。
エラー型の乱立。 指示がなければ、必要な場所ごとに新しいエラー型を定義する。src/error.rs に一元化するルールを明記する。
genericsとtrait objectsの選択ミス。 デフォルトではジェネリクス境界(fn process<T: Trait>(t: T))を選ぶが、動的ディスパッチ(fn process(t: &dyn Trait))が適切なケースもある。プロジェクトの方針をCLAUDE.mdに書いておくと自動で従う。
よくある質問
Claude CodeはRustの借用チェッカーを理解しますか?
理解しています。所有権・ライフタイム・借用について正確に推論できます。ただし、プロジェクト固有の規約(エラーハンドリングパターン・テスト構造・Clippyポリシー)は明示的な指示がないと適用されません。CLAUDE.mdがその橋渡しをします。
Rustプロジェクトにはどのファイルを使うべきですか?
Claude Codeだけ使うなら CLAUDE.md のみで十分。Cursor・Copilot Workspace・Codexも使うなら AGENTS.md をプロジェクトルートに追加してください。このガイドのテンプレートはどちらのファイルにも使えます。
rust-analyzerとClaude Codeはどう連携しますか?
独立して動作します。rust-analyzerはエディタでリアルタイムLSP機能を提供し、Claude Codeは cargo check / cargo clippy のターミナル出力を読みます。コンパイラエラーを修正させたいときは cargo check 2>&1 の出力をそのまま貼り付けてください。
PostToolUse hookはどう設定しますか?
.claude/settings.json の hooks セクションに PostToolUse フックを追加し、Edit|Write ツール使用後に cargo check 2>&1 | tail -20 を実行するよう設定します。これにより、Claude Codeはファイル編集のたびにコンパイラフィードバックを受け取り、借用チェッカーエラーを自己修正できるようになります。