Rust QC
このスキルはRustコードに一つの標準を設定し、3つのモードで適用します。ルールはRustスタイルガイド、Rust APIガイドライン、Microsoftの実用的なRustガイドライン(2026.6)、GoogleのRustスタイルガイド、およびmemchr、jiff、ripgrep、fd、dua-cliに見られる実践に基づいています。これらのソースが矛盾する場合、references/precedence.mdに決定が記録されているため、再議論する必要はありません。
ステップ1: コンテキストの確立(全モード)
コードを書いたり判断したりする前にこれを行います。前提条件が失敗した場合は停止して報告し、欠落したツールチェーンやマニフェストを推測してはいけません。
- クレートルート(最も近い
Cargo.toml)を見つけます。ワークスペース、ライブラリ、バイナリ、またはその両方かどうか、公開されているか(publish = falseまたはワークスペース内でのみ使用、またはFFIを通じて内部使用)を確認します。 - プロジェクト独自のルールを最初に読みます。これらはこのスキルのデフォルトを上書きします:
rustfmt.tomlまたは.rustfmt.toml、clippy.toml、Cargo.toml内の[lints]と[workspace.lints]、edition、rust-version(MSRV)、rust-toolchain.toml、CONTRIBUTING.md、AGENTS.mdまたはCLAUDE.md、およびAI_POLICY.md。AI_POLICY.mdまたはCONTRIBUTING.mdがAIやエージェントの貢献を制限している場合は、そのリポジトリ向けの変更を生成する前にユーザーに伝えてください。 - コードをライブラリとアプリケーションに分類します。API設計ルールはライブラリには必須であり、アプリケーションには強く推奨されます。アプリケーションは直接性よりも汎用性を優先するべきです。
- プラットフォームに注意します。
rust-toolchain.tomlがターゲットをリストしている場合、またはコードがこのホスト以外のプラットフォーム向けの#[cfg(windows)]スタイルのゲートを多用している場合は、--target <triple>を追加してゲートを実行します(そのターゲット向けにリントとドキュメントを実行し、テストはスキップします)。プラットフォームゲートのためにホストでのみ現れるデッドコードなどの警告は、ホストのアーティファクトであり、所見ではありません。 scripts/check.sh <crate-root>を一度実行してベースラインを記録します。これにより、自分自身の変更と既存の負債を区別できます。クレートに相互に排他的な機能がある場合は、デフォルトの--all-featuresではなく--features "<リスト>"を使用します。
ツールチェーン、コンポーネント、ツール(nightly、Miri、cargo-hack)をインストールしたり、ユーザーが要求しない限りクレートのフェッチ以外でネットワークを使用したりしないでください。チェックに欠けているものがある場合は、その旨を伝え、推奨してください。
ステップ2: モードを選択
作成: ユーザーが新しい関数、メソッド、型、モジュール、またはクレートを希望する場合。以下の「コードの作成」に従います。
変更: ユーザーが既存のコードの変更(機能追加、改良、バグ修正、リファクタリング、監査結果の修正)を希望する場合。以下の「コードの変更」に従います。
監査: ユーザーが既存のコードのレビュー、評価、チェックを希望し、問題の特定と修正案を求めている場合。以下の「コードの監査」に従います。監査はユーザーが要求しない限りコードを変更せず、所見を生成します。
リクエストは複数のモードにまたがる場合があります(「このモジュールを監査して、見つけたものを修正してください」)。最初に監査を実行し、報告してから、修正のために変更モードに切り替えます。
非交渉ルール
これらはすべてのモードで適用されます。完全なルールセット(ID付き)は、最後にリストされている参照ファイルにあります。
- フォーマットはrustfmtの仕事です。 rustfmtに逆らって手動でフォーマットしないでください。意図的な配置(テーブル、行列)または生成コード以外では
#[rustfmt::skip]を追加しないでください。 - コードは健全です。 安全なコードは未定義動作を引き起こしてはいけません。すべての
unsafeブロックには、その直前の// SAFETY:コメントがあり、各前提条件が満たされる理由を説明する必要があります。すべてのunsafe fnとunsafe traitには# Safetyドキュメントセクションが必要です。unsafeはFFI、ベンチマークされたパフォーマンスの必要性、または新しい抽象化にのみ使用し、Send、Sync、ライフタイムをバイパスするためには決して使用しないでください。references/unsafe.mdを参照してください。 - エラーとパニック。 入力または環境によって引き起こされる回復可能な障害は
Resultを返します(原因が明らかな場合はOptionも可)。検出されたプログラミングバグはパニックになります。ライブラリコードは、呼び出し元の入力が合理的に無効である可能性がある場合でも、パニックになりません。references/errors.mdを参照してください。 - ライブラリエラーは、テキストを解析せずに呼び出し元が分岐できるようにします。
kind()が公開#[non_exhaustive]列挙型を返す具象エラー型、またはis_*()述語。プライベートフィールドは適切です。アクセサのないプライベートな種別は欠陥です。メッセージは問題のトークンと位置を指し示し、入力全体をコピーしないでください。 - 本番コードでは裸の
unwrap()は使用禁止。?、パターンマッチング、または不変条件が成立する理由を述べるexpect("...")を使用します。unwrap()はテストでは問題ありません。ドキュメント例では、非表示の# Ok::<(), Box<dyn std::error::Error>>(())行とともに?を使用します。 - すべての新しい依存関係はコストであり、dev-dependenciesも含む。 本当の複雑さを取り除くか、合理的に自分で書けない正確さを提供する場合にのみ追加します(Unicodeのケースフォールディングは良い理由ですが、100行のパーサーに対するプロパティテストフレームワークはそうではありません)。その理由をレポートに記述してください。
- 構造はサイズに一致する。 1つの小さな関数とそのエラー型のためにモジュールを作成しないでください。モジュールは、明確な概念といくつかの項目、または数百行のコードでファイルに値します。
- リントのオーバーライドには
#[expect(lint, reason = "...")]を使用し、#[allow]は使用しないでください。これにより、古いオーバーライドが明らかになります。 - 公開アイテムは文書化されています。最初の文は約15語の1行であり、関数には該当する場合は
# Errors、# Panics、# Safetyセクションを含めます。references/documentation.mdを参照してください。 - 公開型は
Debugを実装します。 人が読むことを意図した型はDisplayも実装します。 - コメントは理由を説明し、内容を説明しない。 コメントがコードを言い換えている場合は削除します。ripgrepの
crates/core/main.rsのアロケータコメントがモデルです:決定、理由、およびそれが適用される条件。 - コメントやドキュメント内のASCIIアート図や罫線文字は禁止。 構造を文で説明します。図はスクリーンリーダーで読めず、コードが変更されると腐ります。(チームの慣例であり、上流のものではありません。)
- ドキュメント内の設計ジャーナルコンテンツは禁止。 ドキュメントは現在の動作を説明し、コードがどのようにして生まれたか、またはどのガイドラインに従ったかを説明しません。
- テストは動作を検証します。 定数を言い換えたり、実装の分岐を反映したりするテストはありません。
コードの作成
外部から内部へと作業します:型とシグネチャを最初に、次にドキュメント、実装、テスト。本体の前にドキュメントコメントを書くことは、混乱を招くAPIを発見する最速の方法です。
- 配置する。 アイテムをその概念を所有するモジュールに置きます。小さなクレートでは、それはしばしば
lib.rsです。新しいモジュールはその主要な型にちなんで命名され、コードがそれを正当化する場合にのみ作成されます(ORG-19)。references/organization.mdを参照してください。 - 型でモデル化する。 混乱しやすい値や不変条件を運ぶ値のための新しい型。コンストラクタは不変条件を強制します。呼び出しサイトで意味が明らかでない
boolパラメータの代わりにenum。タプルよりも名前付きフィールド。無効な状態を表現不可能にします。 - シグネチャを形作る。 読み取り専用のものは借用します(
&str、&[T]、&Path、Option<&T>)、保存または消費する場合のみ所有権を取得します。ライブラリでは、摩擦を取り除く場合はimpl AsRef<Path>またはimpl IntoIteratorを受け入れます。アプリケーションでは、具体的な型を維持します。いくつかのオプション設定を持つ型のためのビルダー。build()で検証します。references/api-design.mdを参照してください。 - 命名する。 RFC 430のケーシング、
as_/to_/into_変換、new、with_*、try_newコンストラクタ、foo()ゲッター。あいまいな言葉(Manager、Service、Factory、Helper、Util)は禁止で、同じインターフェース内の既存の意味と衝突する名前も禁止です(NAME-9)。 - 文書化する
references/documentation.mdに従い、公開アイテムには実行可能な例を含め、返す位置や長さの単位を含めます。 - 実装する。 単一目的の関数。条件分岐や外部状態が必要になった場合のコンビネータチェーンよりも単純な
forループ。推論が読者に推測を強いる場合の型注釈。人間のテキストにはUnicode対応の処理:大文字小文字を区別しないマッチングにはUnicodeケースフォールディングを使用するか、そのギャップを文書化します(API-27)。 - エラーを処理する
references/errors.mdに従います:ライブラリでは公開種別または述語を持つ具象エラー型、アプリケーションでは.context()付きのanyhow。 - テストする
references/testing.mdに従います:インラインユニットテスト、tests/下の統合テスト、新しい依存関係を必要としない広範な不変条件チェック(短い入力を列挙するか、単純なリファレンスに対するシード付きループ)。 - 検証する 以下のゲートで。
コードの変更
目標は、触れたコードを少なくとも見つけたときと同じくらい明確に保つ、最小限の正しい変更です。
- 編集前に読む。 関数全体、その呼び出し元、テスト、および近隣のAPIを読みます。ローカルな慣習(インポートスタイル、エラー型、ドキュメントの見出しスタイル(
# Example対# Examplesなど)、行幅、密度、パラメータ名とキー名)を特定し、このスキルのデフォルトと異なっていてもそれに合わせます。 - バグ修正は、バグのクラスをカバーする失敗するテストから始まる。 報告された入力を再現し、同じ根本原因が破壊する近隣のケース(より多くの要素、他の順序、組み合わせ)を追加します。それらが失敗することを確認し、根本原因を修正し、成功することを確認します。プロジェクトが回帰テストを1か所に保管している場合(ripgrepの
tests/regression.rsなど)、そこに追加します。 - 原因を修正し、症状を修正しない。 失敗を黙らせる新しい
unwrap_or_default()、clone()、または早期リターンは、元のパスが間違っていた理由を説明できない限り、修正ではありません。理由がコードから明らかでない場合は、修正箇所に1行のコメントを追加します。 - 外科的に行う。 依頼されていないコードの名前変更、再配置、再フォーマットは行わないでください。無関係な問題を黙って修正するのではなく、ユーザーにリストしてください。
- 公開APIとABIを安定に保つ ユーザーが破壊的変更を承認しない限り。公開フィールドを持つ公開構造体へのフィールド追加、網羅的な公開列挙型へのバリアント追加、必須のトレイトメソッドの追加、またはFFI契約の変更は破壊的です。
- コードとともにドキュメントとテストを更新する。 変更されたシグネチャや動作は、同じ変更で
# Errors、# Panics、例、テストの変更を意味します。 - フォーマット: ベースラインがrustfmt-cleanだった場合は
cargo fmtを実行します。そうでない場合は、変更した部分だけをフォーマットし、ユーザーに伝えます。rustfmt.tomlがnightlyのみのオプションを使用している場合(安定版rustfmtは「nightlyチャンネルでのみ利用可能」と警告します)、プロジェクトの標準はcargo +nightly fmtです。インストールされている場合はそれを使用し、そうでない場合は何も自動的にフォーマットせず、その旨を伝えます。安定版rustfmtはプロジェクトの設定に反してコードを書き換えるためです。 - 監査からの修正: 重要度順に所見を修正し、論理的な変更ごとに1つの所見とし、サマリーに所見の名前を記載します。
- 検証する 以下のゲートで、ステップ1でチェックしたすべてのターゲットにおいて、ベースラインと比較して新しい警告が導入されていないことを確認します。
コードの監査
-
scripts/check.sh --audit <crate-root>を実行します(ステップ1に従って--targetも追加)。--auditパスは、公開APIの到達可能性、文書化されていないunsafe、欠落しているドキュメントと# Errors/# Panicsセクション、テスト外のunwrap、理由のない#[allow]、dbg!/todo!、グロブインポート、メッセージのないアサーションを報告します。これらのhygiene結果を簡潔に報告します:カテゴリ(ドキュメント、Debug、SAFETYコメント、リント設定)ごとに1つのMinor所見。それぞれにカウントと、最も重要ないくつかのインスタンスを付けます。ゲートステップが、ここのツールチェーンがプロジェクトのCIやrust-toolchain.tomlで使用されているものより新しいためにのみ失敗する場合は、ツールチェーンドリフトです:1つのMinor所見であり、Majorではありません。 -
python3 -I scripts/audit_scan.py <crate-root>を実行して、clippyが見えないものを検出します:グロブ再エクスポート、あいまいな型名、ASCIIアートコメント、設計履歴ドキュメント、パラメータテーブル、裸のTODO、空のexpectメッセージ、ライブラリコード内のprintマクロ。コードを読んで各ヒットを確認します。誤検出は破棄します。 -
ユーザーが遭遇する方法で動作を調査する。 これが実際のクレートでユーザー向けのバグのほとんどが見つかる場所なので、監査のほとんどの時間をこことステップ5に費やします。クレートのドキュメント、仕様、テストを読んで、クレートが処理すると主張する入力機能をリストアップします。それぞれについて、既存のテストがカバーしていない小さな現実的な入力を構築し、ワークスペース外の使い捨てクレートから公開APIを通じて実行し、出力をユーザーが得るべきものと比較します。フォーマットが許可するがフィクスチャがスキップする機能をカバーします:
- 継承、間接、またはデフォルト値
- 重複
- オプションまたは非表示の部分
- 他のツールが許容する軽度の損傷
- 非ASCIIテキストとレガシーエンコーディング
- 大きいが正当なサイズ
確認された各問題を、それを示す入力とともに報告します。
-
信頼できない入力を試す。 信頼できないバイトやテキストを解析するすべての公開関数に対して、関連するアルファベット上のすべての短い入力、または数百万のシード付きランダム変異を与え、パニックをキャッチします。また、タイムアウト付きで、作業を入力サイズよりも速く成長させる細工された構造(共有または再帰的な参照、深いネスト)も試します。
-
ツールが判断できないコードを読む: 各
unsafeブロックの健全性、エラー設計、API形状、モジュール境界、命名、テスト品質、コメントが理由を説明しているかどうか。オペレーティングシステムと通信するコードでは、以下もチェックします:- システムコール、I/O、または環境障害でのパニック(戻り値に対する
assert!やunwrap、非UTF-8の環境変数やpasswdフィールド) forkとexecの間のコード(pre_execクロージャ)またはシグナルハンドラ内で、割り当て、ロック、フォーマット、ログ、または終了を行うコード。これらは非同期シグナルセーフではありません- リークしたファイル記述子やハンドル、そして刈り取られ再利用された可能性のあるプロセスIDに送信されたシグナル
- サイズ、カウント、座標に対する狭義のキャストと未チェックの算術
references/audit-checklist.mdを使用します。 - システムコール、I/O、または環境障害でのパニック(戻り値に対する
-
以下の形式を使用して報告します。すべての所見に対して具体的な修正を提案します。
ステップ1と2は迅速なベースラインであり、監査ではありません。それらにかける時間は全体の約5分の1を超えないようにしてください。主にリント、ドキュメント、そして…
…
[元の内容は途中で切れていますが、残りの翻訳が必要な場合は、提供された全文を再送信してください。]