Agent Skills by ALSEL
OpenAIソフトウェア開発⭐ リポ 6品質スコア 73/100

Rust Best Practices Guide

Rustの最新のベストプラクティスを網羅したガイドです。コードスタイル、エラーハンドリング、パフォーマンス最適化、並行処理、プロジェクト構成、依存関係管理、ドキュメンテーション、テスト、セキュリティ、およびCI(継続的インテグレーション)について、実践的な知識を習得できます。

description の原文を見る

A comprehensive guide to modern Rust best practices covering style, error handling, performance, concurrency, project organization, dependency management, documentation, testing, security, and CI.

SKILL.md 本文

指示

一般的なコーディング規約とスタイル

  • 標準的な命名規約に従う: すべての識別子について Rust の命名慣例に従う。型名(構造体、列挙型、トレイト)と列挙型のバリアントは UpperCamelCase を使用する。関数、メソッド、モジュール、変数名は snake_case を使用する。定数と静的変数は SCREAMING_SNAKE_CASE を使用する。例えば、構造体は UserAccount、関数は process_request という名前になる。CamelCase でのすべて大文字の頭字語は避け(UUID の代わりに Uuid を使用)、短縮形より完全な単語を優先する。目的の名前が予約キーワードの場合は、生識別子を使用する(例:r#trait)か、末尾にアンダースコアを追加する。

  • rustfmt によるコード整形: 公式の Rust スタイルガイド(主に rustfmt で実装)に準拠する。インデントに 4 スペースを使用し、行の幅を 100 文字以下に保つ。rustfmt がコードを自動的に整形して一貫したスタイルを実施し、コードレビューでの議論を避ける。CI では、書式チェック(cargo fmt -- --check)を含めて、書式が正しくないコードを拒否する。これにより、プロジェクト全体で統一されたスタイルが保証され、読みやすくなり、コミュニティの一貫性に貢献する。

  • 慣用的な表現: Rust の慣用句を活用して、明確かつ簡潔に。可能な限り中間変数より式を優先する(例:let x = if cond { a } else { b }; を使用し、可変の let x; に if/else ブロックを続けない)。高レベルのコンストラクトで十分な場合、低レベルのループや手動メモリ管理よりもイテレータと所有権システムを活用して明確なコードを書く。コメントは完全な文で書き、行コメント(//)を優先する。公開アイテムには /// ドックコメントを使用し、例と使用ガイダンスを含める(ドキュメンテーションセクションを参照)。

  • モジュールの明確性を保つ: 明確な目的でモジュールを階層的に整理する。src/ の各ファイルはモジュールを定義できます。サブモジュール(独自のファイルまたは mod.rs を持つ)を使用して、コードを論理的にグループ化する。異常なケースを除き、#[path] を使用してファイルをインクルードしない。代わりに Cargo の規約に従う(例:キャンドルルートとして src/lib.rs、サブモジュールはモジュール名の後に付けたファイル)。これにより、プロジェクト構造が予測可能になる。大規模なモジュールを小さなサブモジュールに分割して、ファイルを管理可能なサイズに保ち、関心事を分離する。

  • プロジェクト構造の一貫性: Cargo の慣用的なレイアウトを使用する。パッケージはライブラリクレート(src/lib.rs)や/またはバイナリクレート(プライマリバイナリの場合 src/main.rs、追加バイナリは src/bin/)を含むことができます。ほとんどのコードをライブラリクレートに保つことで、テストと再利用が可能になり、main.rs はライブラリコードをインスタンス化して呼び出すだけにします。複数クレートのプロジェクトの場合、Cargo ワークスペースを検討して、単一の Cargo.lock を共有し、バージョンを調整する。各クレートには明確な責務があるべき。例えば、ウェブアプリケーションでは、コアロジック、HTTP サーバーランタイム、ユーティリティ用の個別のクレートを持つかもしれません。

エラーハンドリングのベストプラクティス

  • 回復可能なエラーには Result を優先: Rust は例外を使用しません。代わりに、回復可能なエラーの標準は Result<T, E> 型です。失敗する可能性のある関数は Result を返し、呼び出し元がエラーを処理するか伝播するかを選択できます。panic! は回復不可能なエラーまたはインバリアント違反(バグを示す状況)にのみ使用します。例えば、ファイルを読み込む関数は、ファイルが見つからない場合にパニックするのではなく、Result<Contents, IOError> を返すべき。エラーをコールスタック上で伝播したい場合は、? 演算子を使用して便利に呼び出し元に返す。

  • panic!Result に関するガイドライン: 妥当な回復方法がなく、実行を続けることが無効またはアンセーフな状況でのみ panic! を呼び出す。これには、内部ロジックエラー、到達不可能なコードパス、または回復不可能な状態の破損が含まれます。特にライブラリコードは、予想されるエラー条件でのパニックを避け、代わりにエラーを返して、ライブラリユーザーが処理方法を決定できるようにします。バイナリ(アプリケーション)では、パニックはプログラムをクラッシュさせるため、真に回復不可能な状況のために予約します。expect/unwrap はテスト、プロトタイプ、またはクイックスクリプトの main でのみ使用し、それでも expect を使用して有用なメッセージを提供することを優先します。

  • エラー型とコンテキストを使用: 列挙型を使用して、ライブラリ用に明確なエラー型を定義します(各バリアントが異なるエラーケースを表す)。thiserror クレートは、カスタムエラー型の std::error::Error トレイト実装を簡単に導出できます。アプリケーションコード(バイナリ)の場合は、シンプルさのために anyhow のような便利なエラーラッパーを使用します。anyhow::Error は、任意のエラーを表現できる動的エラー型であり、バックトレースをキャプチャします。これはきめ細かいエラー処理が必要ない main またはハイレベルのコードで有用です。例えば、CLI ツールの main 関数は fn main() -> Result<(), anyhow::Error> として、異なるエラー型で自由に ? を使用できます。anyhow で、.context("high-level description") または .with_context(|| ...) を使用してエラーにコンテキストを付加し、低レベルのエラーにより多くの意味を持たせます。これは、エラーが発生したときにプログラムが何をしていたかを明確にすることで、デバッグに役立ちます。ライブラリでは、結合化されたエラー(独自の Error 型またはよく知られたエラー型)を返すことを優先し、呼び出し元が応じられるようにします。これは anyhow を使用することに対する利点で、エラーの内部を隠しません。

  • サイレント失敗を回避: エラーを無視しない。Result が未使用の場合、Rust は警告します。? で伝播するか、OkErr の両方のケースを明示的に処理することで処理する。特定のエラーが本当に実行不可能な場合は、最低でもログするか、なぜ安全に無視できるのかを文書化することが望ましい。unwrap_or_elseif let Err(e) = ... でログするなどのメソッドは、そのまれなケースで有用です。

  • パニック安全性と Drop Drop 実装内またはロックを保有している間にパニックするコードを書く際に留意します。これは意図しない結果につながる可能性があります(ミューテックスの中毒やリソース解放なしでの中止など)。リソースを保有している間にパニックを優先しない。そのような状況で Result を使用して失敗を示す。Rust のアンワインドは Drop を実行しますが、デストラクタ自体がパニックした場合、プロセスは中止します。

パフォーマンス最適化テクニック

  • ゼロコスト抽象化: Rust のゼロコスト抽象化を信頼する。低レベルのコードと比較して、パフォーマンスペナルティなしでハイレベルのコードを書くことができます。例えば、イテレータ、クロージャ、ジェネリクスは、手書きのループを超えるオーバーヘッドなしで、コンパイル時に最適化されます。常に明確で慣用的な Rust を優先します。コンパイラの最適化(LLVM)は非常に強力です。証拠のない、過度に複雑またはマイクロ最適化されたコードを書くことは避ける。ハイレベルのイテレータは多くの場合、明示的なループと同じマシンコードにコンパイルされるため、明確性と保守性のために使用します。

  • メモリ管理: 所有権と借用を活用してメモリを管理する。可能な限り、スタック上にデータを割り当てるか、連続構造(Vec など)で割り当て、メモリをフラグメント化する連結構造を使用することは避けます。ほとんどの使用ケースでは、Vec またはスライスが LinkedList よりもキャッシュに優しく、高速になります。データの不要なクローンを避ける。参照またはボローイング(&T)を使用してデータをコピーなしで渡す。高コストなクローン操作がある場合は、Rc/Arc(共有所有)や Cow(コピーオンライト)などのスマートポインタを使用して最適化を検討します。ただし、Rc/RefCell には注意が必要です。スレッド間で共有する場合は、Rc がスレッドセーフでないため、Arc<Mutex<T>> を使用します。ホットパスで割り当てのオーバーヘッドが見つかった場合は、プーリングまたは割り当ての再利用を使用します(例:Vec::with_capacity で事前割り当て、または bytes のような特定のケースでバッファを管理するライブラリを使用)。Rust は細かいメモリレイアウト制御を提供します。smallvecarrayvec などのクレートを使用して、特定のケースでヒープ割り当てを回避できます。

  • インライン化とコンパイラヒント: コンパイラは、より高い最適化レベルで、特に小さい関数またはジェネリクスで多くの関数を自動的にインライン化します。パフォーマンスクリティカルなスポット(例:非常にホットな小さな関数)で #[inline] または #[inline(always)] を使用して、インライン化を示唆します。インライン化は関数呼び出しのオーバーヘッドを排除し、呼び出し境界の向こうのさらなる最適化を有効にします。ただし、影響を測定します。インライン化は時々バイナリサイズを増やすか、コードを膨張させるか、他の最適化を防ぐなど、パフォーマンスに害を与える可能性があります。コンパイラがそれらを不必要にインライン化している場合は、めったに使用されない重い関数で #[inline(never)] を使用します。また、コールドコードパス(例:エラーハンドリング)を #[cold] でマークして、ホットパスを良くして最適化できます。インライン化を調整した後、常にベンチマークして、それが役立つか確認してください。

  • ベンチマーキング: 適切なベンチマークとプロファイリングを使用して、最適化を指導する。Criterion クレートは、組み込みの #[bench] がナイトリーを必要とするため、堅牢なマイクロベンチマークを書くための一般的な選択肢です。perf、Intel VTune、Windows のパフォーマンスアナライザーなどのツールを使用してコードをプロファイリングし、真のボトルネックを見つけます。cargo flamegraph または同様を使用してホットスポットを可視化することを検討します。アルゴリズムの複雑さを最初に最適化し(例:良い複雑さを持つ適切なアルゴリズムとデータ構造を使用)、その後、必要に応じて内部ループをマイクロ最適化します。メモリ使用状況とアクセスパターンに注意してください。メモリバウンドのコードは、算術の細かい調整よりも、キャッシュに優しい構造から利益を得ることができます。

  • ゼロコスト FFI と SIMD: 外部 C/C++ コードを呼び出す必要がある場合、FFI で呼び出しますが、Rust の安全性を尊重します(unsafe と正しい extern 宣言を使用)。FFI 呼び出しのオーバーヘッドは通常最小限です(関数呼び出しに似ている)ため、抽象化の意味でゼロコストですが、タイトなループで FFI 境界を横断することに注意してください。CPU 集約的なタスクでは、Rust の安定した SIMD サポート(std::arch 内)または packed_simd などのクレートを使用することを検討してください。ただし、SIMD がアドレスできるボトルネックを明確に識別した後のみです。高レベルの Rayon(データ並列処理)またはスレッドは、明示的な SIMD を書くよりも多くのケースで適用しやすいかもしれません。

並行処理と非同期プログラミング

  • 所有権を通じた恐れのない並行処理: Rust の所有権モデルは、デフォルトでスレッドセーフを確保します。他のスレッドに送信しても安全なタイプは Send トレイトを実装し、スレッド間で参照を共有しても安全なタイプは Sync を実装します。ほとんどのプリミティブ型と標準コレクションは SendSync です(その内容がそうである限り)。Rc または RefCell(スレッドセーフでない)のような型を使用する場合、コンパイラはそれらをスレッドに送信することを防ぎます。共有データに対してスレッドセーフな代替品(ArcMutexRwLock)を優先します。可能な限り共有状態を最小化するように常に設計します。頻繁にロックする代わりに、メッセージパッシングまたはチャネル(例:std::sync::mpsc または crossbeam-channel などのクレート)を優先して、データを転送します。これにより、競合と可能なデッドロックが減ります。

  • スレッドと同期: シンプルなマルチスレッド処理に対して、標準ライブラリからハイレベルの並行処理コンストラクトを使用します。std::thread::spawn でスレッドを並列タスク用にスポーン、JoinHandle を使用してそれらを結合します。共有データを Mutex<T>(相互排除の場合)または RwLock<T>(複数の読み手が許可される場合)で保護します。より複雑な待機条件には Condvar を使用します。多くのケースでは、複雑なロックより、メッセージパッシングが望ましい。チャネルは安全なキューを提供し、1 つのスレッドはデータを送信でき、別のスレッドは受け取ることができます。Rust のデータレース安全性を念頭に置いてください。Send/Sync を正しく使用してコードが コンパイルされると、実行時にデータレースがないことが保証されます。これは大きな勝利です。ただし、ロジックレース(ハイレベルロジックの競合状態)は回避すべきです。

  • 非同期プログラミング(async/await): 高い並行性ネットワーキングまたは I/O バウンドなタスクの場合、async/await の使用を検討します。Rust の Async は協調的な並行処理です。Tokio または async-std のような非同期ランタイムは、多くのタスクを少数のスレッドで実行し、フューチャーをポーリングすることで。Tokio はエコシステムで最も広く使用されている非同期ランタイムで、豊富なエコシステム(タイマー、ネットワーキングなど)とデフォルトではマルチスレッドスケジューラーを提供しています。async-std は、Node.js に触発された同様の API を提供し、特定の使用ケースの代替案ですが、Tokio は本番システムの事実上の標準になっています。非同期コードを書くときは、#[tokio::main](または同等)を使用してランタイムを開始し、async fn フューチャーで .await を使用します。スポーンするタスクが Send であることを確認します(Tokio のマルチスレッドスケジューラーはデフォルトでスポーンされたフューチャーが Send であることが必要です)。!Send フューチャーがある場合(例えば、RcArc の代わりに使用)、current-thread ランタイムで実行するか、tokio::task::LocalSet を使用する必要があります(Tokio はそのような場合のために spawn_local も提供します)。非同期コードの並行性は、複数のタスクが実行されて .await ポイントで制御を譲るから来ています。

  • 非同期並行処理に関する考慮事項: 非同期タスクはスレッドプール上で実行されますが、非同期関数内で長いブロッキング操作を実行してはいけません。これはエクゼキューター全体のスレッドをブロックします。任意のブロッキング I/O または CPU バウンドのワークについて、他の非同期タスクをスタルすることがないように、専用スレッドプール(Tokio は spawn_blocking を提供)にオフロードします。非同期用に設計された同期プリミティブを使用します。例えば、std Mutex の代わりに tokio::sync::Mutex(リアクターのブロッキングを防ぐため)、または tokio::sync::mpsc を非同期チャネルのために使用します。非同期コードは、共有リソースを更新する 2 つのタスクがあるなど、同期されていない場合、ロジックにまだ競合状態を持つことができます。調整するために Arc<tokio::sync::Mutex<_>> またはチャネルを使用します。また、キャンセルに注意します。待機されたフューチャーは、呼び出し元がそれをドロップした場合、キャンセルされるかもしれません。ハーフダンの操作を処理する必要がある場合は、クリーンアップコードまたは drop_guard パターンを書いてください。ハイレベルライブラリを活用します(Tokio の select! マクロ、または futures クレートユーティリティなど)複数の同時実行タスクとタイムアウトを管理するために。

  • Send と Sync マーカートレイト: 並行処理に関連して SendSync トレイトを理解します。Send は値を別のスレッドに安全に転送できることを意味し、Sync は値への参照をスレッド間で共有しても安全であることを意味します。Rust は Send/Sync パーツで構成されるタイプについては自動的に実装します。アンセーフコードを書くまたは FFI とやり取りする場合、カスタムタイプが正しく実装する(またはしない)ことを確認して、スレッドセーフを保持します。タイプが絶対にスレッドセーフであるかどうか確実でない限り、Send または Sync を手動で実装しないでください。これはアンセーフです。通常は、コンパイラの自動実装に頼り、タイプに含まれる T: Send などを示す必要がある場合は std::marker::PhantomData を使用します。

プロジェクト構造とモジュール整理

  • クレートとパッケージ: クレートは Rust の コンパイルユニットで、パッケージ(Cargo パッケージ)は Cargo.toml を持つ 1 つ以上のクレートのセットです。関心事を明確に分離するか、再利用を有効にするために個別のクレートを使用します。例えば、プロジェクトはコアライブラリクレートとそのライブラリを使用するバイナリクレートを持つかもしれません。各クレートはキャンドルルートソースファイル(src/lib.rs または src/main.rs など)を持っています。デフォルトでは、cargo new は バイナリクレート(main.rs)を持つパッケージを作成します。src/lib.rs も追加する場合、これは同じ名前のライブラリクレートを定義します。ほとんどのコードについてこのライブラリを使用し、main.rs を最小限に保ってください。これはテストと再利用を促進します。

  • モジュールと可視性: モジュール(mod)を使用してクレート内のコードを整理します。モジュールはスコープと可視性を制御できます。プログラムの主要コンポーネント(例:networkdatabasehandlers など、ライブラリのサブモジュール)についてハイレベルのモジュールで開始します。各モジュール内で関連する型と関数をグループ化します。デフォルトでは、モジュール内のアイテムはプライベート。pub キーワードを使用してモジュール境界でアイテムを公開し、クレートの公開 API(ライブラリの場合)またはその他のモジュール(バイナリの場合)の一部とします。ライブラリでは、きれいな公開 API を目指してください。Rust API ガイドラインは実装の詳細をプライベートに保つことや、一貫した最小限のインターフェースを公開することを推奨しています。

  • ファイル整理: ファイルシステムへのモジュールマッピングについて、Cargo 規約に従います。例えば、lib.rsmod parser;srcparser.rs または parser/mod.rs からコードをロードします。サブモジュールを個別のファイル(モジュールが小さい場合)または mod.rs を持つサブディレクトリ(またはニューインラインファイルの命名を使用、例えば parser/mod.rsparser.rs プラス parser/ のサブモジュールでもできます)として整理する。一貫性が鍵です。多くのプロジェクトは今、mod.rs ファイルを避けスタイルをフラットに使用します(Rust 2018+ は foo.rs のモジュールをサブモジュールと共に foo/bar.rs で許可し、mod.rs なし)。1 つのスタイルを選択して固守してください。より大きなプロジェクトについて、共通 Cargo 設定と依存関係を共有する Cargo ワークスペースでクレートをグループ化します。ワークスペースの各クレートは独自のサブディレクトリにあり、ワークスペース Cargo.toml がそれらを集計します。

  • 整理のためのモジュール可視性: 内部ヘルパー関数と構造体の可視性を管理するために pub(crate)pub(super) を効果的に使用します。これにより、実装の詳細をテストできます(同じモジュール内にテストを宣言することで、または #[cfg(test)] mod tests を使用)しながら、公開 API から隠し続けます。これは、ライブラリのより清潔な API 表面とロジックのカプセル化につながります。

依存関係管理と Cargo フィーチャー

  • Cargo.toml ベストプラクティス: Cargo セマンティクスを使用してバージョンで依存関係をピンすします(キャレット要件がデフォルト、例:foo = "1.2.3"1.x と互換性があることを意味する)。依存関係で semantic Versioning を尊重します。デフォルトでは、Cargo は最新の semver 互換バージョンを選びます。バイナリアプリケーションの場合、再現可能なビルドを確保するために Cargo.lock をチェックインすることが推奨されています(ロックファイルは正確なバージョンを固定)。ライブラリについて、歴史的には Cargo.lock を無視する慣行でしたが、ダウンストリームクレートが最新の互換バージョンを使用できるように。最近、ガイダンスは「プロジェクトに最適なことをしてください」に柔らかくなりました。ライブラリの CI テストの一貫性のためにロックファイルをコミットするかもしれませんが、ライブラリとして依存関係として使用されるときに無視されます。どちらの場合でも、cargo update を定期的に実行し、最新のバージョンをテストして、どんな破損を早期にキャッチしてください(CI がこれを自動化するのを助けることができます)。

  • オプション依存関係のフィーチャーの使用: Cargo フィーチャーフラグを活用して、依存関係をオプションにし、条件付きコンパイルを制御します。Cargo.toml で optional = true としてめったに使用されない依存関係をマークし、名前付きフィーチャーでグループ化します。例えば、クレートに JSON シリアル化機能がオプションがある場合、[dependencies] serde = { version = "1.0", optional = true } および [features] json = ["serde"] があるかもしれません。この方法で、ユーザーは "json" フィーチャーを有効にしてオプトインでき、デフォルトでは軽量クレートに保つ。フィーチャーを明確かつポジティブに命名(フィーチャーは機能を追加する)。ネガティブな命名を避ける(例えば「no-std」の代わりに「std」という名前のフィーチャーを使用し、デフォルトでオンにして、オフにできます)。フィーチャーは依存関係グラフ全体で統一されている(任意のクレートがクレートのフィーチャーを有効化した場合、そのプログラムのすべてのユーザーについても有効になります)ため、加法的で互換性のあるように設計します。README またはドキュメントでフィーチャーを文書化して、ユーザーがオプション機能を有効にする方法を知るようにします。

  • 依存関係の膨張を回避: 各依存関係はコンパイル時とバイナリサイズを増やす可能性があります。軽量クレートと標準ライブラリを可能な限り優先します。新しい依存関係を追加する前に、本当に必要か、フィーチャーゲート処理できるかを検討します。依存関係ツリーに注意を払ってください(cargo treecargo tree -e features)大きなまたは重複している依存関係を特定します。Cargo フィーチャーを使用して、依存関係の未使用部分をカットダウンします(いくつかの人気のあるクレートは、デフォルトの重い機能を無効化するフィーチャーを持ちます)。例えば、serde を使用する場合、デフォルトフィーチャーを無効化し、導出マクロだけが必要な場合は serde/derive だけを有効化できます。また、ビルドのみの依存関係と開発依存関係を観察します。開発依存関係を本番以外のニーズに限定します(それらはダウンストリームクレートに含まれることはありませんが、コンパイル時間に影響を与えます)。

  • バージョン競合の処理: Cargo のバージョン解決のおかげで、semver に依存して依存関係が動作し続けることに一般的に頼ることができます。特定のバージョンが必要な場合(例えば、セキュリティフィックスまたは API 変更)、不等号要件(>== など)または直接 gitpath 依存関係を一時的なオーバーライドのために使用できます。ただし、維持管理者/上流で動作して、リリースされたバージョンの使用を保つことを優先します。ライブラリが広く使用されている場合、最小サポート Rust バージョン(MSRV)のポリシーを検討し、Cargo.toml(rust-version フィールド)とドキュメントに注記してください。これにより、依存関係のアップグレードが警告なしに新しいコンパイラを必要としなくなります。

  • マルチクレートプロジェクト用ワークスペース: プロジェクトに複数の相互依存クレート(例えば、ライブラリと複数のバイナリユーティリティ)が含まれる場合、Cargo ワークスペースを使用します。これにより、共有 Cargo.lock と出力ディレクトリが可能に、ビルドを高速化し、依存関係バージョンをクレート間で一貫性を保ちます。ルートに Cargo.toml を置いてリスト [workspace] メンバーを配置、メンバーの個々の Cargo.lock ファイルを削除します(ワークスペースはトップレベルで 1 つ使用)。ワークスペースは、すべてのクレート間でコマンドを実行しやすくするためにも(例えば、cargo fmt はすべてのメンバーで実行)、すべてのクレートを実行するため使用します。

ドキュメンテーションとテスト規約

  • Rustdoc ドキュメント記述: すべての公開アイテム(公開構造体、列挙型、関数、モジュールなど)は、その目的、使用方法、任意の重要な詳細を説明する rustdoc コメント(///)を持つべき。クレートルート(lib.rs またはバイナリの main.rs)で、モジュールレベルのドックコメントを含める、クレートの機能と使用例の概要を提供(ライブラリについては、これはユーザーが docs.rs で見る最初のページです)。主要機能についてすべてのドキュメントに例を含めることを目指してください。Rust API ガイドラインは、可能なことほぼすべての公開アイテムについて例を含めることを推奨しています。ドキュメントにドックテストコードブロックを使用します。トリプルバッククォート付きの任意のコード rust は、cargo test でテストされて、正確であり続けることを確保します。例:
    /// 長方形の面積を計算します。
    /// 
    /// # 例
    /// ```
    /// let rect = Rectangle::new(5, 10);
    /// assert_eq!(50, rect.area());
    /// ```
    

このサンプルはコンパイルおよび実行されます(出力は ///# 行を使用してセットアップを隠す限り、必須ではありません)。ドックサンプルを unwrap() または expect() を使用するのではなく、失敗可能な呼び出しについて ? 演算子を優先するように記述して、適切なエラーハンドリングを奨励します。ドックサンプルが fn main() -> Result<(), Box<dyn std::error::Error>> { ... } ラッパーで完全なプログラムのようにコンパイルできるように書くことで、隠された #use ...; を使用することで、ドックに表示されませんが、? を使用できます。

  • パニック、エラー、安全性のドキュメント: 公開関数がパニックできる場合(例えば、内部的な unwrap を持つかまたは無効な入力でパニックします)、ドックコメントの「# Panics」セクションでこれをドキュメント化します。同様に、関数が Result を返すか、エラー条件があった場合、どの状況でエラーが返されるか説明する「# Errors」セクションを追加します。アンセーフ関数またはトレイト実装については、呼び出し元または実装者がアップホールドしなければならないインバリアントを説明する「# Safety」セクションを含めます。例えば、unsafe fn do_io(ptr: *mut u8, len: usize) を持つ場合、ポインタが len バイトのために有効である必要があることをドキュメント化します。このドキュメントは、ユーザーが API を正しく使用する方法を知るための重要で、将来のメンテナー(自分自身を含む)が推論を覚えるためのものです。Clippy は、# Safety セクションなしで pub unsafe fn に警告する linter を持っています。これは良い慣行を有効にするためです。

  • テスト戦略: Rust テスト規約に従います。#[cfg(test)] mod tests モジュール追加によってコードと同じファイルで単体テストを書きます。単体テストは機能の小さな単位(個別の関数またはタイプ)に焦点を当てるべき。#[test] 関数を使用してテストを定義する。アサーション(assert_eq!、assert! など)を使用して動作を検証する。エラーをチェックする必要があるテストについて、テストが Result<()> を返し、便宜性のために ? を使用できます(Result が Err を返すどんなテストが失敗します)。

  • 統合テスト: より大きなスコープのテスト、またはパブリック API の外部パースペクティブからテスト については、tests/ ディレクトリの統合テストを使用します。tests/*.rs ファイルを作成します。各ファイルはライブラリクレートに自動的に依存する個別のクレートとしてコンパイルされます。これでは、クレートをインポート(例:use my_crate::*;)テストをこれは外部ユーザーのようにであるかのごとく書きます。統合テストは、公開 API が使用可能で、エンドツーエンドで動作することを確保します。これはバイナリアプリケーション(例:assert_cmd クレートを使用してバイナリを実行し、出力をチェックして)テストに特に有用です。cargo test を実行するのは、すべての単体テスト、統合テストをコンパイルして実行し、ドックテストも抽出して実行します。個別のファイル焦点異なるエリア(例えば、tests/api.rs は API エンドポイント、tests/cli.rs は CLI インターフェーステスト)で統合テストを個別のファイルに保ちます。

  • ドキュメンテーションテスト: 記されているように、doctests は自動的に実行されます。ドックのサンプルコードが実際に動作することを確認してください。cargo test を実行します。時々、特定のドックテストをスキップまたは無視する必要があるかもしれません(例えば、失敗することを意図しているか、長時間実行)、/// ```rust,ignore または no_run を追加することで(コンパイルされるがない実行)。これらをめったに使用しない。理想的には、ドックテストが可能なら実行すべきです。なぜなら、例とテスト二重として機能するからです。

  • テスト整理: テスト関数を明確に命名してカバーするものを示します。サブモジュールまたは単純にコード内の近接を使用して関連テストをグループ化できます。複数のテストについて共通セットアップが必要な場合、標準ライブラリのテストフィクスチャサポートを使用することを検討します(テスト関数内でセットアップを行うか、ヘルパー関数を抽出します。組み込み JUnit スタイルセットアップ/ティアダウンがありませんが、Rust のスコープルールでセットアップとリソースをドロップできます)。

  • 継続的テスト: テストスイートをよく実行(例えば cargo test またはテストファイルの変化で実行する cargo watch のようなツール)。重要なコードの高いカバレッジを保つ。コードカバレッジツール(cargo tarpaulin または cargo LLVM coverage など)を使用ギャップを識別するのにはいいですが、意味のあるテストの代価でカバレッジ数値を追うのはしない。エッジケース、典型的な使用、修正バグの回帰テストに焦点を当ててください。

セキュリティと安全考慮事項

  • 可能な限りアンセーフを回避: Rust のセキュリティ保証は、セーフコードに留まるとき最も強力です。絶対に必要な場合(例えば、C とのやり取り、ボローチェッカーが理解できないデータ構造の実装、またはパフォーマンスクリティカルな低レベルの命令呼び出し)にのみ、コードのセクションをアンセーフとしてマークします。すべてのアンセーフブロックまたは関数は、Rust のセキュリティルール(データレース、無効なポインタ逆参照、不正なアライメント、ユースアフターフリーなし)をアップホールドしているというプロミスです。アンセーフを使用する場合は、カプセル化します。セーフ抽象化の背後に隠す、アンセーフがユーザーに漏らさないように。例えば、メモリプールを実装する場合、すべてのアンセーフをモジュール内に保ち、セーフ関数を内部的に安全インバリアントを確保する公開します。

  • アンセーフコードガイドラインに従う: アンセーフコードを必ず書く場合、新興 Rust Unsafe Code Guidelines や Rustonomicon(The Dark Arts of Unsafe Rust)を参照して、ベストプラクティスを習う。例えば、一つのガイドラインはアンセーフ関数のすべてのセキュリティ要件をドキュメント化(誰が呼び出すかはそれらをアップホールドする必要)を明確にコメントとドックで。別のガイドラインは、アンセーフブロックのサイズを最小化することです。アンセーフ操作をだけ内の実行してセーフコードで多くを行う。Miri(未定義の動作を検出できるインタープリター)を使用してアンセーフコードをテストして問題をつかむ。サニタイザーサポート(例えば、ASan または ThreadSanitizer、Rust フラグ経由)でメモリ問題をデバッグするときに実行します。

  • Clippy Lints: 一般的な間違いをキャッチしてコード品質を改善するため cargo clippy を定期的に使用します。Clippy はコード正確性、パフォーマンス、スタイル、およびそれ以上のリンタのコレクションです。unwrap をコードで使用する場合(適切なエラー処理で扱うのに良い)、または効率的なメソッドが存在するとき、悪い方法を使用することについて警告できます。例えば、Clippy は手動ループ代わりに iter().any() を使用して要素を検索することを提案、またはあなたが Option または Result を不要にクローンするときキャッチするかもしれません。少なくとも clippy::pedantic または clippy::nursery リントを有効にしたい場合、より厳格なチェック、ただしいくつかのリントはきわめて主観的に気をつけてください。特定 Clippy リント があなたのプロジェクトに適用されないか、ノイズが多すぎる場合、それらを明示的に許可できます(属性またはクリッピー.toml config)。重要なことは Clippy を使用して慣用的なパターンを学び、潜在的なエラーをキャッチすることです(うっかり std::fmt::Debug の実装を忘れるか、浮動点数で直接 == を使用する)。

  • アンセーフを制限して監査: 可能な場合、アンセーフなしですませるクレート についてクレートレベルリント #![forbid(unsafe_code)] を保つ。これはアンセーフを不意に導入しないことを確保します。プロジェクトがアンセーフを使用する必要がある場合(例えば、低レベルモジュール)、コードベースの小さい部分に分離することを検討します。コードレビューは、アンセーフブロックに特に注意を払ってください。外部監査ツールを使用するか、それらセクションについて徹底的に査読を持つ同僚。コミュニティの理念は、アンセーフがハイパフォーマンスまたは FFI のため必要な時がある認めることが、それは極端な注意と精査で処理されるべきことです。

  • メモリセーフティと所有権: 所有権モデルを抱きかかえることで、自然にバグの全体的なクラスを避けます(バッファオーバーフロー、二重フリー等)。C コードとやり取り或いは手動メモリ管理をしている場合、常に警惕してなるべく早くセーフ Rust タイプに生ポインタを変換します。例えば、C から生ポインタを得た場合、slice::from_raw_parts などのアンセーフブロックでそれをスライスにラップし、それからセーフに動作します。可変グローバル状態を避けます。グローバル状態が必要な場合、lazy_static または once_cell を使用してセーフに初期化、またはより良く、状態を関数を通して渡すようにリファクタリングします。スレッド・ゼロセーフを高レベルプリミティブを優先してデッドロックと他のスレッドセーフ問題を回避。例えば、どこにでも Arc<Mutex<T>> を散らすのではなく、各スレッドまたはタスクが必要とするデータの排他的所有を持つように設計するのでスレッドタスクしてください。

  • セキュリティ監査とツール: 既知のセキュリティ脆弱性の依存関係をスキャンするために cargo audit を使用します。RustSec advisory データベースは、使用しているクレートバージョンがセキュリティ欠陥を持つ場合、警告します。CI に cargo audit を統合(実行はクイック)して新しい脆弱性が開示されたときに通知されます。依存関係を最新に保つ、特にセキュリティフィックスについて。暗号化またはセキュリティセンシティブコードについて、よく査読されたクレート(crypto のための ring、または sodiumoxide)を使用することを優先して、あなたが専門知識を持つ限り独自に書かない。機密情報を処理する場合、サイドチャネル考慮事項に気をつけます(例えば、シークレットについて定時比較を使う)及び標準的なメモリをドロップで0にされていない事実(シークレット用 zeroize cレート使用)。

  • 疑わしい構造のリント: Rust のコンパイラと Clippy は、セキュリティに役立つリントを提供します。例えば、コンパイラは未処理 must_use 結果について警告(多くの場合、処理されなかったエラー)、ロジック間違いをキャッチできます。Clippy は println! をライブラリで使用(デバッグレフトオーバーかもしれない)や dbg! マクロなどについて警告できます。リリースビルドについて #![deny(warnings)] を有効化することを検討(ただし CI については、上記のように Clippy を使用するのがしばしば良い)。cargo-deny もあり、依存関係での license compliance や他のセキュリティ関連のポリシーをチェックできます。

コードレビューと CI 実践

  • コードレビュー焦点: Rust コードレビューで、正確さ、明確さ、慣用的な使用に焦点を当てる。すべての可能な失敗ポイント(ライブラリコードで .unwrap() または .expect() なし、アプリケーションコードでの利用は正当化される)にエラーハンドリングが済んでいることを確保します。関数がドキュメント、特に公開を持つことをチェック、及び命名は明確です。各アンセーフ利用がサウンドであることを検証(レビュアーはそれぞれ推論または説明を要求することを試みるべき見えないなら)。過度に複雑なコード;多くありますより簡単な慣用的なアプローチ(Clippy ヒントがここをガイド)。関数がコードカバレッジをテストで、テストと新しいコードをすべてパスを確保します。パフォーマンスが変更されたコードについて懸念事項であれば、ベンチマークが存在するか追加すべきか、変更がプロファイルされているか、議論してください。

  • CI での自動チェック: 各プルリクエストとメインへのプッシュで実行される継続統合パイプラインをセットアップします。CI は最低限実行すべき cargo build(すべてのターゲットフィーチャーのクリーン環境でコンパイルするか確認)、cargo test(ドック・統合テスト含まれて)、cargo fmt -- --check(フォーマット実施するため)、及び cargo clippy(リント清潔さを実施)。clippy については、-D warnings を使用するかもしれない CI ビルドを失敗させるどんなリント警告も作ります。これは、マージされたすべてのコードが標準に自動的に従うことを保証します。さらに、CI での cargo audit を実行して、依存関係脆弱性を早期にキャッチすることを検討します。プロジェクトがライブラリの場合、複数の Rust バージョン(特に MSRV を約束する場合、及び最新の安定版)で、可能ならば異なるプラットフォーム(Linux、Windows、Mac、またはこれらをサポートするなら WASM ターゲット)でテストすることができます。

  • 継続的デプロイメント / 統合: 可能な場合、CI を実行して、追加の品質チェック:例えば、cargo fuzz を使用してファジテスト(クラッシュまたはパニックケースをキャッチ)、proptest クレートでプロパティベースのテスト、またはステージング環境での統合テスト。これらはすべてのプルリクエストで実行されないかもしれませんが、毎晩や要望で実行されるかもしれません。ライブラリについて、例がコンパイルすることを確保(cargo test --examples)し、おそらく cargo doc が警告なしで成功していることをます。

  • プルリクエスト チェックリスト: 貢献者に一般的なことを思い出させるプルリクエストテンプレートまたはチェックリストが有用です。ユーザーが直面する変更のため更新されたドキュメント、新フィーチャーまたはバグフィックスのためのテスト、スタイル ガイドラインの接着(ただし CI は整形/Clippy 問題をキャッチ)。これはレビュープロセスをスムーズに保つ。可能な場合、小さく焦点を当てたプルリクエストを奨励、これは徹底的にレビューするのにより簡単です。

  • CI アーティファクトとカバレッジ: オプションで、ドキュメント生成およびアップロードのために CI を使用します(docs.rs はすでにライブラリのリリースに対してドックを構築)。アプリケーション、CI はクロスコンパイルまたはマトリックスビルド))を使用して様々なターゲットについてバイナリを構築し、セキュリティ監査または静的解析を実行することもできます。いくつかのプロジェクトは新しいコードが最小のテストカバレッジを持つか、少なくともカバレッジを減らさないことを強制。Codecov のようなツールを統合するためにプルリクエスト上で報告することができます。コードカバレッジは完璧なメトリックスではありませんが、テストされていないコードパスを特定するのに役立つことができます。

  • リリース慣行: リリースする時期になった(ライブラリについて crates.io に発行;バイナリについて、新しいバージョン/タグをカット)、Cargo.toml のバージョンを semantic バージョニング(ライブラリの場合)またはリリース準備に従ってアップデートします。CI(またはローカルテスト)はコンパイルしたいコマンドについてフルテストスイートを実行して、ほぼリント実行してリリースモードを確保します。いくつかのパフォーマンスセンシティブなテストは cargo test --release を使用することを検討(ただし、ほとんどのロジックテストはデバッグで良くします)。デプロイメント及び CI が必要に応じて発行またはアーティファクトをビルドして、自動化により(例えば、CI での cargo publish 正しい認証情報を使用しライブラリ)、またはバイナリを構築や GitHub リリースに付加します。

これらのガイドラインに従うことで - 明確で慣用的なコード記述、堅牢なエラーハンドリング、賢い最適化、Rust の並行処理を安全に使用、プロジェクトをきれいに構造化、思慮深い依存関係管理、徹底的なドキュメント、CI を活用 - あなたは信頼性があって、保守しやすい、効率的で、での仕事を楽しめる Rust コードを作成できます。幸いな Rust!

ワークフロー

これは、Rust プロジェクトでこれらのベストプラクティスを適用する段階的なワークフローです:

  1. 初期セットアップ: cargo new で新しいプロジェクトを開始。すぐにバージョン管理(例えば git)をセットアップし、プッシュの各テスト、整形、及び Clippy を実行する CI コンフィギュレーション(GitHub Actions、GitLab CI等)を追加。開発環境内で rustfmt と Clippy を有効化(rustup component add rustfmt clippy)。

  2. **規約を確立:

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
sheinsight
リポジトリ
sheinsight/spack
ライセンス
MIT
最終更新
2026/5/7

Source: https://github.com/sheinsight/spack / ライセンス: MIT

関連スキル

汎用ソフトウェア開発⭐ リポ 39,967

doubt-driven-development

重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 1,175

apprun-skills

TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。

by yysun
OpenAIソフトウェア開発⭐ リポ 797

desloppify

コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。

by Git-on-my-level
汎用ソフトウェア開発⭐ リポ 39,967

debugging-and-error-recovery

テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

test-driven-development

テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

incremental-implementation

変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。

by addyosmani
本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: sheinsight · sheinsight/spack · ライセンス: MIT