
2026/10/22 4:11
Google の Rust スタイルガイド
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
本書は、Google における Rust プログラミング言語の使用を支援する包括的なスタイルガイドであり、基本的なフォーマットを超えて、API デザイン、所有権、並行処理、およびプラットフォーム固有の実装要件(Android、ChromeOS、Fuchsia、Chromium を含む)に関するガイダンスを含んでいます。主な目標は、広大なソフトウェアエコシステム内でのコードの可読性、保守性、そして大規模な変更への対応を最優先することにあります。これらのルールは、通常、公式の公開 Rust スタイルガイドから派生しており、組織的な目標や独自のコードベース要件と衝突しない限りそのように採用されています。より広い Rust エコシステムに沿いつつ、多様なプラットフォームに適応することで、google3、Chrome、Android のようなプロジェクト全体で一貫性を確保しています。これらの慣約を遵守することは、Google ソフトウェアスタック全体の品質を大幅に向上させ、新規開発者のオンボーディングを容易にし、大規模なアップデートの円滑化を実現します。結局のところ、本書は安全性、エラーハンドリング、テストフレームワーク、並行処理モデルに関する技術的制約など、既存のエコシステムとの整合性を保ちつつ、すべての新コードが従うべき現在のルール集として機能します。
本文
Google Rust スタイルガイド
背景と目標
スコープ
- 対象コードベース:Android、Chromium および関連するプロジェクト。
- このガイドは他とのコードベースとの協議に基づいて開発され、適用の意向がある。
- コードベースごとの独自の厳密さに関する規則が存在する可能性がある。
目標
C++ スタイルガイドと同様に、以下の目標から派生したスタイル規則を採用する:
- 可読性と保守性を優先:記述性よりもコードの長期的な維持可能性を重視する。
- 自動化と一貫性を支援:大規模な変更(LSCs)の実施を容易にする。
- Rust エコシステムとの整合:既存の Rust 慣習に依存し、学習コストを削減する。
- 実利のある規則のみ:各開発者が常に心に留める具体的な利益をもたらす規則を採用する。
- 議論の回避:合理的な意見の相違が生じやすく、再論争が起きるスタイル問題を防ぐ。
ガイドの適用方針
- 公的な Rust スタイルガイドに従う:本ガイドと抵触しない限り、公式な Rust の慣習(例:
で強制される形式)を優先する。rustfmt- 「抵触」とは、本ガイドが推奨しないパターンを採用したり、Rust 公式ガイドラインに反する事例を含むことを指す。
- 大規模変更(LSCs)への対応:意図的な再構成フレンドリーなパターンを採用し、ツールとの親和性を高める。
API デザインとコードの組織化
コードの種類による適用方針
| コードタイプ | 方針 | 留意点 |
|---|---|---|
| ライブラリコード | 必須:API デザインに関する規則を厳格に適用する。 | 汎用的な再利用性を重視し、抽象化を避けないようにする。 |
| アプリケーションコード | 推奨:強力な推奨事項として扱う。 | 特定の課題解決のためには単純さと直観性を優先する。過剰な一般化を避ける。 |
Crate とモジュールの構成
- Crate(クレート):コンパイル単位であり、常にルートモジュールを含む。
- Rust モジュール:Crate 内のコード組織単位。名称の可視性を制御し、ファイルやディレクトリ構造と連動する。
クレートサイズの推奨事項
- 小さなクレートを好む:理想は単一のファイル(一つの抽象化に焦点)である。コードを可能な限り小さなクレートに分割する。
- Chromium では高粒度のライブラリターゲットが推奨される理由:ビルドグラフの依存関係管理、並列コンパイルの効率化。
- 大きなクレートの例外:トレイト実装の整合性が必須な場合や、大規模な基盤ライブラリ構築時のみ許容される。
ファイルレイアウトのルール
- フラットな構造を推奨:
(一つの Crate)は単一のディレクトリに置かれる。rust_library- サブディレクトリでファイル整理を行わないこと。クレートが大きくなりすぎた兆候とみなされ、分割すべきである。
- 例外(//third_party):Cargo も併せてビルドされる場合のみ、標準の Cargo ディレクトリ構造を使用する。
名前付け規則
- リーフモジュール:プライマリユーザー定義項目の名前に基づいて命名する(例:
,mod foo
)。foo.rs- 特定の抽象化ではない場合は任意の名前でも可。
- ノンリーフモジュール:API を実装せず、宣言と再エクスポートのみを含むべき。
- 例外:少量の「ものをまとめる」実装コードは OK。
- 許可されるサブモジュール:
,mod test
など。#[cxx::bridge]
インポートと使用法
グローブインポート(Globs)の使用制限
以下の状況を除き、グローブインポートを使用しないこと。
(インラインモジュール用)use super::*
(Enum メソッド本体など)use enum::*
(ノンリーフモジュール用)pub use submodule::*- C++ 互換性のため:名前空間統合に使用する際のみ(例:
のようなアクセス)。absl::Status
根拠
- グローブインポートはコード精査者の負担を増加させる。
- xrefs が存在しないため、型がどこから来るか把握しにくくなる。
Use ステートメントの形式
を設定として採用する。imports_granularity = Module- 悪い例:ルートとネストを混ぜる
use foo::{Foo, bar::Bar, baz::Baz}; - 良い例:階層を保つ
use foo::{Foo, bar::{Bar, Baz}};
ローカルスコープでのトレイトインポート
- ファイル全体で使われない限り、関数内部でのみトレイトをインポートする。
fn foo(x: u8) { use std::str::FromStr; // 関数内でのみ let string: String = x.parse(); }
トップレベル関数の呼出方
- 他のモジュール/クレートからのトップレベル関数は、モジュール名付きで呼び出す。
use std::mem; let x = mem::transmute(y); // 推奨 - 例外:
- C++ のフリー関数など十分に区別された名前を持つ場合。
- 小さい単一目的のファーストパーティクレートからの場合。
- モジュール名衝突が避けられない場合。
タイプ名の参照方法
- 原則:デフォルトでモジュールパスを使用せず、トップレベル型名(例:
)を参照する(Regex
でインポート)。use regex::Regex - 例外:
- 曖昧さ回避が必要な場合(例:
)。io::Error - リネームが必要な場合(例:
)。use std::io::Error as IoError - 多数の HIR タイプを含む
モジュールなど。hir - マクロや生成コード、多くの依存関係がある場合。
- 曖昧さ回避が必要な場合(例:
インポートメカニズム
- Chromium では
を使用する。chromium::import!()
コードフォーマットと美しさ
フォーマット設定
- 行幅:100 列 (
)max_width = 100 - ヘックスリテラル:小文字 (
)hex_literal_case = Lower - マクロマッチャーを整形する (
)format_macro_matchers = true - 不可視文字のエスケープを使用(
など)。\u{200D}
自動フォーマットの除外(#[rustfmt::skip]
)
#[rustfmt::skip]以下の状況のみ許容される:
- 手動整列:可読性を保つために行を整列させる場合、行列構築関数など。
- 生成されたコード:
を使用して管理する場合。genrule - 禁止:個人好みに基づく除外は行わないこと。
所有権と借用
スマートポインタの回避
- Rc と Arc を避ける:ユニークな所有権と借用を優先する。
- 例外:代替構造が複雑化しすぎてしまう場合のみ使用(レビュー時に議論が必要)。
コピー戦略
- 深層コピーよりも借用を優先:
は借用関係の複雑化による実用的選択を除き、避けるべき。.clone() - Rc/Arc のクローンは例外:参照カウントポインタのクローンにはこのアドバイスは適用しない。
関数の引数と戻り値の設計
柔軟な参照タイプを使用
データ自体への参照を優先し、スマートポインターやラッパーへの参照を避ける。
| 使用する | 代わりに | 理由 |
|---|---|---|
| | ボックス内のデータを借用するのではなく、直接借用する方が効率的。 |
| | 任意のスライスを受け付ける方が汎用性が高い。 |
| | 文字列スライスを受け付ける方が汎用性が高い。 |
| | Option 内のデータを直接借用する。 |
イテレータの優先
- 戻り値:
よりも、イテレータ(またはVec<T>
)を返すことを優先する。impl Iterator- 例外:呼出側が所有ベクトルを必要とする場合、実装が自然にベクトルを構築する場合、ランダムインデックスが必要な場合など。
- 引数:スライス
よりも、イテレータまたは&[T]
を受け付けることを優先する。impl IntoIterator- 理由:コピー不要、柔軟性、チェーンとの親和性。
Into の使用
- ライブラリ API で
ではなくT
を考慮する場合もあるが、コード肥大化とのバランスに注意する。impl Into<T> - 例外:アプリケーションコードは原則として
を使用しないこと。impl Into<T>
エラー報告の選定
| メカニズム | 使用場面 | 例 |
|---|---|---|
| Option | 原因が明確で、さらなる詳細は不要な失敗。欠落やパース不整合など。 | |
| Result<T, E> | 回復可能な失敗で、呼出側がエラーハンドリングが必要(I/O など)。 | |
| panic! | バグや不変違反(回避不可能なエラー)。コードの欠陥を示す。 | インデックス越え時など。 |
- Enum を bool より優先:パラメータの目的が明確でない場合は、bool ではなく Enum を使用する。
- API コントラクト:
、Option
、Result
のいずれかを選択し一貫させる。panic!
タイプとトレイト
新タイプ(Newtype)の活用
以下の場合は型エイリアスの代わりに newtype を使用する:
- 顕著な意味的違いがある場合。
- 独自の API を持つ必要がある場合。
- 将来の基礎タイプ変更の柔軟性を望む場合。
- 安全性のため混同を避ける必要がある場合(例:
とDate
)。Day
命名フィールドの優先
- タプルインデックス(
)よりも命名フィールドを持つ構造体を使用する。.0- 例外:Newtype タプル構造や、文脈内で混乱がない場合。
トリート実装と定義
実装の順序
- 低レベルライブラリ:ユーザーのニーズを予期し、トレイト実装を提供する。
- アプリケーションコード:必要に応じてトレイト実装を追加する。
メソッド対トレイト
- 標準ライブラリの目的に一致するメソッドがある場合は、そのトレイトを実装する(例:
)。FromStr - 手動実装より
を優先する。#[derive()]
ジェネリクス対 dyn トリート
- ライブラリコード:ジェネリクス(
)をT: Trait
より好む。dyn Trait- 理由:効率性(vtable の不要化)、一部トレイトは Dyn 互換ではないため。
トリート定義の制限
以下の目的以外にトレイトを定義しない:
- 汎用プログラミング(
)のため。T: Trait
/impl Trait
を扱うため。dyn Trait- 拡張トレイトを定義するため。
- それ以外は固有の実装を使用する。
拡張トレイト(Extension Traits)
- 目的:異なるクレートのタイプにメソッドを追加するためだけのものであり、一般化や動的ディスパッチは意図しない。
- 命名:接尾辞
を付ける(例:~Ext
)。ForeignTypeExt - 所有権:元のタイプを持つチームが拡張トレイトも保持すべき。
標準トレイトの実装方針
| トリート | ポリシー | 注意点 |
|---|---|---|
| Copy | 常に実装(可能なら)。管理を単純化する。 | 非 Copy フィールドを持つ場合は不可。 |
| Clone | 外部リソースを含まない限り、ほぼ全ての実装。 | - |
| Default | またはデフォルト状態を持つ場合。 | - |
| PartialEq / Eq | ユーザーが を使う場合は (詳細な比較)から始める。 | 反射性が必要な場合 も実装。 |
| PartialOrd / Ord | / を使う場合は から始める。 | 完全順序化が必要なら 。 |
| Hash / Eq | マップキーまたはキャッシュ対象の場合。 | - |
| From | 重要な構築方法を持つ場合、 も追加推奨。 | - |
オプション構造体と Default
- 全ての公開フィールドを持つ構造体が「オプション袋」として使われる場合、
を実装する。Default- 理由:構文
の利用が可能になる。Struct { ..Default::default() }
- 理由:構文
From 実装とコンストラクタ
- 主要な構築方法の一つとして期待される重要な
実装を持つ場合、専用メソッド(From
など)も追加する。::new()
公開 API の安全性
- Enum:他のチームが使う場合は
を適用する。(オントロジー定義や 2 状態のみなど例外あり)#[non_exhaustive] - Struct:全ての公開フィールドを持つ場合は
を適用する。(FRU 構文使用など例外あり)#[non_exhaustive]
並行性とマクロ
Chromium 特有の制限
- async / .await を使用しない。
- シーケンス(std::sync):新しいスレッドを開始する代わりに使用する。
- Mutex:Fuchsia 向けには
を使用。fuchsia_sync::Mutex
マクロとコード生成
以下の優先順位で API を設計する:
- 通常の関数、型、トレイト、モジュール(マクロ回避)。
マクロ。macro_rules!- 他のコード生成戦略。
。proc_macro
- 理由:IDE 機能の低下、開発者の学習コスト増、大規模変更の困難さ。
命名規則
メソッドと関数
- Rust 標準ライブラリおよび Naming Guide を従う。
- 接頭辞:変換系は
、as_...
、to_...
を使用。into_... - 初期化関数:コンストラクタは
、結果型はnew()
。値対値変換はtry_new()
。from_foo()
ライフタイム
- 記述的な名前を付ける(特にタイプライフタイム)。
- 例外:
のような一般的な抽象化、匿名(Cow
)、単一文字のローカル境界。'_
ジェネリクス
- 単一または多文字の短い名前を使用し、対象する種類に関連付ける。
、T
は比較的非縛り文脈で可。U
クロージャーを受け付けるアクセッサ
.with_foo() アクセッサは以下の状況で推奨:
- プロパティが Mutex/RefCell などでラップされており、スコープを制御する場合。
- プロパティの変更後に検証が必要な場合(クローンが高価な場合など)。
Getter / Setter の命名
| 目的 | 関数名 | 説明 |
|---|---|---|
| 取得 | | を返す。 |
| 可変取得 | | を返す。 |
| 取り出し | | 値を取り除き、古い値を返す(None など)。 |
| 設定 | | 新しい値を設定する。 |
| 置換 | | 新しい値を設定し、古い値を返す。 |
コンテキスト文字列とアサーション
- アサーションの理由や文脈を明示する(
)。assert!(cond, "message")- 誤用フラグ、バグ報告、ログ出力などに役立つ。
- 禁止:単に条件を繰り返すだけのメッセージ(例:"values should always be sorted")は避ける。
条件付きとイテレータチェーン
- 直接反復が明らかな場合:イテレータコンビネータチェーンは避ける。
ループヘッダーにチェーンを組まない(変数に割り当てる)。for- 複雑なチェーンは可読性を損なうため、明示的な
ループが推奨される。for
- 例外:チェーン自体がロジックを提供する場合や、簡潔に表現できる場合。
パニックとコード実行
- catch_unwind を使用しない:Chrome/Android/Fuchsia は
環境のため。panic=abort- 例外:深層防御の試み(レビュー推奨)。
- main() 前の実行禁止:静的初期化順序など、
より前にコードを実行させない(main()
クレート使用禁止)。ctor
unsafe コードと不安定機能
コードは同等の safe な実装がない限り書かない。unsafe- 安全性コメントと技術的正当性を記述する。
- 不安定機能を原則として使わない(特定プロジェクトでのみ例外)。
ドキュメント化
コメントの役割
公開 API のドキュメントに
/// または //! を使用する。実装コメントには // を使用し、両方を混合してよい。
記載すべき項目
- 例:関数の動作イメージを与える十分な例を含む(全てではないが、クレートの挙動を把握できる)。
- パニックケース:入力が不適切な場合の動作を明示する。
- unsafe / 不変:安全上の注意事項や制約を明記する。
- キャピタライゼーション・句読点:正確に使用する。
記載すべきでない項目
- プログラミング言語の基礎知識の説明(例:「FileSystem はモックです」)。
- コードがすでに表している事項への単なる繰り返しは避ける。
ドキュメントコメントの書式
- ///:関数、構造体、Enum などの項目向け。
- //!:モジュールまたはライブラリ自体向け(ファイル上部)。
- Markdown 構文の使用を推奨する。
よりマクロ生成ドキュメントの方が読みやすい。///
テストとビルド
テストフレームワーク
| プラットフォーム | フレームワーク | 特徴 |
|---|---|---|
| Chrome | | C++ テストバイナリとの統合。 |
| Fuchsia | | Fuchsia CI 連携。 |
タイプ推論と明示
- 型推論を過剰に使用しない。変数宣言に明示的な型を優先し、Turbofish を避けられる場合は避ける。
- 推奨追加:汎用戻り値(
,.into()
)、複雑なイテレータチェーンなど。.parse()
- 推奨追加:汎用戻り値(
パターンマッチングの活用
よりパターンマッチングを優先し、パニック回避をコンパイラーに証明する。unwrap// 推奨:コンパイラ検証 if let Some(value) = option { use_value(value); }
Expect の使用
- パニックが必要な場合は、理由を記載した
を使用する(expect
より)。unwrap
マッチ式の完全性
Enum に対しては、ワイルドカードアームを含めるか#[non_exhaustive]
/if let
を使用し、欠落したバリアントを明示する。match ... else {}
その他の実装上の注意点
Overload の制限
- 数学的・論理的な目的以外での演算子のオーバーロードは避ける(シンタックスシュガーとしてのみ使用しない)。
const 関数の慎重な使用
を付与するのは、将来も常時 const でなければならない場合や、移行を支援する場合のみ。const- 破壊的変更となるため。
Math以外のオーバーロード回避
- 単にコードを簡潔にするためにシンタックスシュガー(演算子オーバーロード)を使うのは避ける。
#[must_use]
の適用
#[must_use]- 関数:戻り値を無視すると適切に使えない場合、エラー情報を返す場合などに適用する。
- 既にエラー型で注釈付け済みなら不要。
- タイプ:遅延評価された計算やエラーを表すタイプに適用する。
#[non_exhaustive]
の例外
#[non_exhaustive]以下の場合は
#[non_exhaustive] を使用しない:
- オントロジーを記述する場合(例:Color)。
- 2 つの状態のみを持つ場合(例:Visible/Hidden)。
- LSC を通じて破壊的変更を受け入れる場合。
#[doc(hidden)]
のアクセス禁止
#[doc(hidden)]- プライベートとして扱い、コードの所有権内でのみアクセスする。
まとめ:コードレビュー時のチェックポイント
- スタイルガイドとの整合性:公式 Rust ガイドラインに抵触しないか確認。
- Crate サイズと構造:フラットなファイルレイアウト、適切なモジュール分割。
- 借用と所有権:Rc/Arc 回避、深層コピーの最小化。
- API デザイン:イテレータ優先、明確なエラー報告(Option/Result/panic)。
- ドキュメント:公開 API の明瞭な説明、実装コメントとの区別。
- テスト:適切なフレームワークの使用、パターンマッチングによる安全性の確認。