mcpField
R
rust-best-practices

rust-best-practices

github.com/existential-birds開発8578 回インストール13 週間前に更新
リポジトリ

rust-best-practices について

rust-best-practicesは、慣用的で安全かつ効率的なRustコードを書くための開発スキルです。コードレビューではなく、実装パターンと検証可能なチェックを開発者に案内します。

主な機能

  1. 01所有権とコーディング慣用句
  2. 02ClippyとLintの運用
  3. 03ベンチマークと性能指針
  4. 04ジェネリクス、ディスパッチ、非同期API
  5. 05型状態とAPI設計
  6. 06ドキュメントとエラー処理

活用シーン

  1. 01所有権とポインター方式の選択
  2. 02Lintとドキュメント検査の追加
  3. 03性能改善の主張の測定
  4. 04公開APIと非同期APIの設計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/existential-birds/beagle --skill rust-best-practices

インストール後、Claude Code で/rust-best-practicesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api-design.md
clippy-config.md
coding-idioms.md
documentation.md
ecosystem-patterns.md
generics-dispatch.md
performance.md
pointer-types.md
type-state-pattern.md

詳細

Rust のベストプラクティス

慣用的で、高速かつ安全な Rust コードを書くためのガイダンスです。これは開発向けのスキルであり、レビュー向けのスキルではありません。レビューではなく、開発時に使用してください。

クイックリファレンス

トピック基本ルールリファレンス
所有権基本的には借用し、独立した所有コピーが必要な場合にのみクローンしますreferences/coding-idioms.md
Clippyすべてのコミットで cargo clippy -- -D warnings を実行し、ワークスペースの lint を設定しますreferences/clippy-config.md
パフォーマンス推測せず、測定します。まず --release でプロファイリングします。大規模な場合は、モノモーフィゼーションとキャッシュラインのアライメントに注意しますreferences/performance.md
ジェネリクス基本的には静的ディスパッチを使い、異なる型を混在させる必要がある場合は動的ディスパッチを使いますreferences/generics-dispatch.md
型状態無効な操作をコンパイルエラーにする必要がある場合は、型システムで状態を表現しますreferences/type-state-pattern.md
ドキュメントなぜかを説明するには //、何をするかと方法を説明するには ///、モジュール/クレートの目的を説明するには //! を使いますreferences/documentation.md
ポインター所有権の要件とスレッドモデルに基づいてポインター型を選びますreferences/pointer-types.md
API 設計直感的で、柔軟かつ明確で、制約があるものにします。不変条件を型で表現し、隠れた契約(再エクスポート、自動トレイト)に注意しますreferences/api-design.md
多彩なパターンドロップガード、拡張トレイト、インデックスポインター、クレートのプレリュードなど、成熟したクレートで実証された慣用パターンですreferences/coding-idioms.md
エコシステムクレートを評価し、エラー処理の戦略を選び、最新の状態を保ちますreferences/ecosystem-patterns.md

ゲート

証拠が必要な成果を主張する前に確認する、合格条件付きの短い手順です(内部的な「確認しました」ではありません)。

Clippy の警告がないこと
  1. ワークスペースのルートから(または -p <crate> を指定して)、次を実行します:cargo clippy --all-targets --all-features -- -D warnings。
  2. 合格条件: 終了ステータスが 0 で、Clippy の deny による失敗なしにコマンドが完了することです。
パフォーマンスに関する主張
  1. 提供時または測定時と同じプロファイルで、cargo build --release(またはベンチマークハーネス)を使ってビルドします。
  2. 同じツールと指標(両方の名前を明記)で変更前と変更後の数値を記録します。例:Criterion の ns/iter、heaptrack のアロケーション数、またはディスク上のフレームグラフのパス。
  3. 合格条件: 両方の測定値を提示できること、または、変わったのは正しさや読みやすさだけであり、パフォーマンスの差は主張していないと明示することです。
変更したシンボルのドキュメント
  1. 編集したクレートに対して cargo doc --no-deps を実行します(ワークスペースでは -p <crate> を追加します)。
  2. 合格条件: ドキュメントのビルドが成功することです。#![deny(missing_docs)](またはクレートのポリシー)が適用される場合は、変更したシンボルに新たなドキュメント不足エラーがないことです。

コーディングの慣用パターン

.clone() よりも &T を優先し、パラメーターには &str/&[T] を使い、インデックスベースのループではなくイテレーターを連結します。Option/Result では、早期リターンに let Ok(x) = expr else { return } を使い、伝播には ? を使います。スコープ内の状態変更には、mem::replace または scopeguard::defer! とともにドロップガード(let _guard = ...。let _ = ... は使わない)を使います。外部の型にメソッドを追加するには、拡張トレイトを使います(trait MyExt; impl<T: Bound> MyExt for T)。グラフや木の形状には、ライフタイムを回避するため、&T の代わりにインデックスポインター(slotmap::DefaultKey、世代付きインデックス)を優先します(unsafe は使いません)。使いやすい glob インポートのために、簡潔なクレートのプレリュードを整備します。プレリュードへの追加は semver のマイナーバージョン変更に相当します(RFC 1105)。所有権、イテレーター、インポートのパターン、およびこれらのエコシステムレベルの慣用パターンについては、references/coding-idioms.md を参照してください。

エラー処理

失敗する可能性がある操作では Result<T, E> を返します。ライブラリのエラー型には thiserror を、バイナリには anyhow を使用します。? でエラーを伝播し、テスト以外では unwrap() を使用しないでください。Option/Result のパターンについては、references/coding-idioms.md を参照してください。

Clippy の徹底

コミットごとに cargo clippy --all-targets --all-features -- -D warnings を実行します。ワークスペースの lint は Cargo.toml で設定し、lint を抑制する際は標準として #[allow] ではなく #[expect(clippy::lint)] を使用します。抑制が不要になったときに警告が出るためです。lint の設定と主要な lint については、references/clippy-config.md を参照してください。

パフォーマンスを意識する

必ず --release でベンチマークを実行し、最適化の前にプロファイリングを行い、ループ内でのクローンや早すぎる .collect() の呼び出しは避けます。小さな型はスタック上に置き、ヒープ割り当ては再帰的な構造や大きなバッファーに限ります。大規模なワークスペースでは、モノモーフィゼーションの予算(型に依存しない内部関数を切り出す、ピーク時のインライン化が重要でない場合は内部のジェネリクスを dyn に切り替える)とフォールスシェアリング(スレッドごとのアトミック変数には #[repr(align(64))] または crossbeam::utils::CachePadded を使用し、Apple Silicon では align(128) を使用する)に注意します。criterion でベンチマークを実行し、ベースラインを保存して(--save-baseline main)、CI で比較します。また、criterion::black_box を使用し(ポインター入力には as_ptr() を使用)、I/O は iter_batched のセットアップに分離します。プロファイリングツール、メモリ割り当ての指針、モノモーフィゼーションのパターン、キャッシュラインのアラインメント、criterion の運用については、references/performance.md を参照してください。

ジェネリクスとディスパッチ

ゼロコストのモノモーフィゼーションを実現するため、デフォルトでは静的ディスパッチ(impl Trait/<T: Trait>)を使用します。dyn Trait に切り替えるのは、異種のコレクションやプラグインアーキテクチャが必要な場合に限り、所有権が不要であれば Box<dyn Trait> より &dyn Trait を優先します。edition 2024 では、-> impl Trait はデフォルトでスコープ内のすべてのライフタイムをキャプチャします。キャプチャを細かく制御するには + use<'a, T> を使用します。静的ディスパッチには、async-trait クレートよりもトレイトでネイティブの async fn を優先します。ディスパッチのトレードオフ、RPIT のキャプチャ規則、async トレイトの指針については、references/generics-dispatch.md を参照してください。

型状態パターン

無効な操作がコンパイルエラーになるよう、有効な状態を型システムで表現します。必須フィールドを持つビルダー、プロトコルの状態機械、ワークフローパイプラインで使用します。実装パターンと使用を避けるべき場面については、references/type-state-pattern.md を参照してください。

ドキュメント

理由には //、公開 API の説明や使い方には ///、モジュールの目的には //! を使用します。すべての TODO に関連する issue へのリンクを付け、ライブラリクレートでは #![deny(missing_docs)] を有効にします。公開トレイトにカスタムコンパイラーエラーを設定するには、#[diagnostic::on_unimplemented] を使用します。ドキュメントテストのパターン、コメントの規約、診断属性については、references/documentation.md を参照してください。

API 設計

次の4つの原則に従います。予測しやすいこと(標準的な名前や特性を再利用する)、柔軟であること(ジェネリクスと impl Trait を使い、不必要な制約を避ける)、明快であること(不変条件を型システムに組み込み、誤用をコンパイルエラーにする)、そして制約を設けること(長期的に維持できるものだけを公開する)。今後拡張する可能性がある型には #[non_exhaustive] を使い、破壊的変更なしに拡張する必要があるトレイトはシールし、外部の型は新しい型でラップして SemVer の対象範囲を制御します。再エクスポートした外部型、-> impl Trait を介した自動トレイトの伝播、意図せず !Send になる Future などの隠れた契約に注意し、公開型に対するコンパイル時の is_normal<T: Sized + Send + Sync + Unpin>() テストで固定します。新しいトレイトを提供するときは、&T/Box<T> に対するブランケット実装を早い段階で用意します(後から追加すると破壊的変更になります)。失敗する可能性があるクリーンアップでは、Result を返す明示的な close()/shutdown() を公開します。Drop は失敗できず、.await もできません。ビルダーパターン、シールされたトレイト、オブジェクト安全性の仕組み、Deref の適切な使い方、失敗するデストラクター、SemVer への影響については、references/api-design.md を参照してください。

エコシステムのパターン

クレートは、直近のダウンロード数の傾向、メンテナンス状況、ドキュメントの品質、推移的依存関係の重さを基準に評価します。ライブラリのエラー型には thiserror、バイナリには anyhow、独自のエラーレポーターが必要な場合は eyre を使います。小さな機能のために重い依存関係を取り込むクレートは、ベンダー化するか自分でコードを書くことを優先します。ライセンスと脆弱性の監査には cargo-deny を、未使用の依存関係の削減には cargo-udeps を実行します。クレートの評価基準、エディションの移行、必須ツールについては、references/ecosystem-patterns.md を参照してください。

ポインター型

所有権とスレッドに応じてポインター型を選択します。単独所有のヒープ割り当てには Box<T>、共有所有には Rc<T>/Arc<T>、内部可変性には Cell/RefCell/Mutex/RwLock を使います。lazy_static や once_cell の代わりに LazyLock/LazyCell(1.80 以降で安定版)を使います。シングルスレッドとマルチスレッドの選択表全体と移行のガイダンスについては、references/pointer-types.md を参照してください。

デストラクターとクリーンアップ

Drop::drop(&mut self) はエラーを返せず、.await もできません。失敗する可能性があるクリーンアップ(I/O のフラッシュ、ネットワークのシャットダウン、非同期コミット)では、Result<(), Error>(または impl Future)を返す明示的な close() または shutdown() メソッドを公開し、フォールバックとして Drop でベストエフォートのクリーンアップを実行します。「Drop 内で self を消費する」パターンには、mem::take を使った Option<T> の新しい型、フィールドごとの mem::take、または ManuallyDrop<T> があります。Drop 内で block_on(...) を呼び出してはいけません(非同期ランタイムでデッドロックします)。スコープ内の状態変更(一時的な切り替え、復元、パニック時の実行)には、let _guard = ... で束縛したドロップガードを使います。すぐにドロップされてしまう let _ = ... は使わないでください。実績のある選択肢は scopeguard::defer! です。ドロップガードは panic = "abort" では実行されない点に注意してください。明示的なデストラクターパターンについては、references/coding-idioms.md と references/api-design.md を参照してください。

非同期 API

async fn は、匿名の impl Future を返すステートマシンに展開されます。公開 API の Send 性に関する契約は、-> impl Future<...> + Send + '_(または &self に結び付けた明示的なライフタイム)で固定してください。-> impl Trait を介した自動トレイトの伝播は暗黙的であり、.await をまたいで保持される単一の Rc<...> または std::sync::MutexGuard によって、Future 全体が !Send に降格し、下流の呼び出し元が動作しなくなる可能性があります。Future をマルチスレッドエグゼキューター(例:tokio::spawn)にスポーンする必要がある場合に限り、+ 'static を追加してください。すべてのシグネチャに一律に 'static を指定すると、&self からの借用が禁止されるため、デフォルトとしては適切ではありません。Drop はキャンセルと同じです。Future がポーリング途中でドロップされると、ローカル変数はドロップされ、クリーンアップは実行されません。すべての公開 async fn について、キャンセル安全性をドキュメントに記載してください。キャンセル安全(recv、観測のみ)か、キャンセル非安全(read_exact、write_all、ポーリングをまたぐ不変条件を保持するもの)かを明記します。ランタイムに依存しないライブラリーコードでは、impl Future を受け取るか、futures クレートのプリミティブを使用してください。内部でスポーンしてはならず、必要なランタイムをドキュメントに記載してください。スタックに固定するローカル Future には std::pin::pin! マクロを使用し、ヒープ割り当てを許容できる場合に限り Box::pin を使用してください。

コメント