命名
大文字・小文字の使い分けがRFC430に従っている (C-CASE)
Rustにおける基本的な命名規則はRFC 430に記述されています。
Rustは「型レベル」のもの(型やトレイト)にCamelCase、
「値レベル」のものにsnake_caseを使用する傾向があります。
正確には、次表のように命名します。
| アイテム | 規則 |
|---|---|
| クレート | 不明 |
| モジュール | snake_case |
| 型 | CamelCase |
| トレイト | CamelCase |
| Enumのバリアント | CamelCase |
| 関数 | snake_case |
| メソッド | snake_case |
| 一般のコンストラクタ | new または with_more_details |
| 変換を行うコンストラクタ | from_some_other_type |
| マクロ | snake_case! |
| ローカル変数 | snake_case |
| スタティック変数 | SCREAMING_SNAKE_CASE |
| 定数 | SCREAMING_SNAKE_CASE |
| 型パラメータ | 簡潔なCamelCase、大抵は大文字で1文字: T |
| ライフタイム | 短いlowercase、大抵は1文字: 'a, 'de, 'src |
| Feature | 不明、ただしC-FEATUREを参照 |
頭字語や複合語は、CamelCaseでは一語に数えます。例えばUUIDではなくUuidを使い、USizeではなくUsize、StdInではなくStdinを使って下さい。
snake_caseではis_xid_startのように小文字にします。
snake_caseまたはSCREAMING_SNAKE_CASEでは、
それが最後の文字を除いて一文字で区切ってはいけません。
例えば、b_tree_mapではなくbtree_mapとしますが、PI2ではなくPI_2とします。
クレート名の前後に-rsや-rustを付けるべきではありません。
全てのクレートがRust製であることは分かりきっています!
そのことをユーザに常に知らせ続ける必要はありません。
(訳注: レポジトリ名等ではなく、Cargo.tomlで指定するクレート名についての指針です)
標準ライブラリでの例
この規則は標準ライブラリの全体に渡って使用されています。
変換メソッドにas_, to_, into_を使っている (C-CONV)
変換メソッドの名前には次のプレフィクスを付けるべきです。
| プレフィクス | コスト | 所有権 |
|---|---|---|
as_ | 低い | 借用 -> 借用 |
to_ | 高い | 借用 -> 借用 借用 -> 所有 (Copyでない型) 所有 -> 所有 (Copy型) |
into_ | 可変 | 所有 -> 所有 (Copyでない型) |
以下に例を挙げます。
str::as_bytes()はコストなしに、strをUTF-8バイト列として見たビューを返します。 入力は借用された&strで出力は借用された&[u8]です。Path::to_strはOSの与えるパスのバイト列に対し、コストの高いUTF-8チェックを行います。 入出力はともに借用されています。小さくない実行時コストがあるため、これをas_strと呼ぶことはできません。str::to_lowercase()はUnicode規格に沿ってstrを小文字に変換したものを返します。 この処理は文字列のデコードを含み、またメモリの確保も行うでしょう。 入力は借用された&strで出力は所有されたStringです。f64::to_radians()は浮動小数点数で表された角度を度からラジアンに変換します。 入力はf64です。これが&f64でないのは、コピーに殆どコストが掛からないためです。 入力が消費されないので、このメソッドをinto_radiansと呼ぶのはミスリーディングです。String::into_bytes()はStringがラップしているVec<u8>を取り出します。 この処理にコストは掛かりません。このメソッドはStringの所有権を得て、所有されたVec<u8>を返します。BufReader::into_inner()はバッファリングされたreaderの所有権を得て、ラップされていたreaderを取り出します。 この処理にコストは掛かりません。バッファ内のデータは破棄されます。BufWriter::into_inner()はバッファリングされたwriterの所有権を得て、ラップされていたwriterを取り出します。 この処理にはコストの掛かるバッファのフラッシングが必要なことがあります。
as_やinto_の付くような変換メソッドは一般に抽象を弱めます。
内部表現を公開したり(as)、データを内部表現に分解したり(into)することになるからです。
一方で、to_と付くような変換メソッドは一般に抽象レベルを保てることが多いですが、
内部で何らかの処理を行いデータの表現方法を変換する必要があります。
ある値をラップして高レベルの意味を持たせるような型において、
ラップされた型にアクセスさせる手段はinto_inner()メソッドによって提供されるべきです。
これは例えば、バッファリングを提供するBufReaderや、
エンコード・デコードを行うGzDecoder、
アトミックなアクセスを提供するAtomicBoolといった型のようなセマンティクスを持つ型に適用できます。
変換メソッドの名前にmutを含めるときは、返り値の型の記述と同じ順番にしてください。
例えばVec::as_mut_sliceはミュータブルは名前の通りスライスを返します。
例えばas_slice_mut等よりもこのような命名が推奨されます。
# #![allow(unused_variables)] #fn main() { // Return type is a mut slice. fn as_mut_slice(&mut self) -> &mut [T]; #}
標準ライブラリでのさらなる例
Getterの名前がRustの規則に従っている (C-GETTER)
後述するような例外を除き、getterの名前をget_で始めるべきではありません。
# #![allow(unused_variables)] #fn main() { pub struct S { first: First, second: Second, } impl S { // Not get_first. pub fn first(&self) -> &First { &self.first } // Not get_first_mut, get_mut_first, or mut_first. pub fn first_mut(&mut self) -> &mut First { &mut self.first } } #}
getという命名は、getterによって得られるものが自明である場合にのみ使用されるべきです。
例えば、Cell::getはCellの中身を返します。
境界検査といった、実行時のバリデーションが必要なgetterには、
unsafeな_unchecked版も用意できないか検討してください。
そのようなメソッドの宣言はふつう、以下のようになります。
# #![allow(unused_variables)] #fn main() { fn get(&self, index: K) -> Option<&V>; fn get_mut(&mut self, index: K) -> Option<&mut V>; unsafe fn get_unchecked(&self, index: K) -> &V; unsafe fn get_unchecked_mut(&mut self, index: K) -> &mut V; #}
getterと変換(C-CONV)の間の違いは往々にして微かなもので、
常に境界がハッキリしている訳ではありません。
例えばTempDir::pathは一時ディレクトリのファイルシステム上のパスのgetterとして捉えられますが、
TempDir::into_pathは一時ディレクトリを削除する責任を呼び出し側に移す変換メソッドです。
このような場合pathはgetterなので、get_pathやas_pathと呼ぶことは正しくありません。
標準ライブラリでのさらなる例
std::io::Cursor::get_mutstd::ptr::Unique::get_mutstd::sync::PoisonError::get_mutstd::sync::atomic::AtomicBool::get_mutstd::collections::hash_map::OccupiedEntry::get_mut<[T]>::get_unchecked
イテレータを生成するメソッドの名前がiter, iter_mut, into_iterとなっている ([C-ITER])
RFC 199に従ってください。
Uという型の要素を持つコンテナの場合、イテレータを生成するメソッドは次のように命名するべきです。
# #![allow(unused_variables)] #fn main() { fn iter(&self) -> Iter // Iter implements Iterator<Item = &U> fn iter_mut(&mut self) -> IterMut // IterMut implements Iterator<Item = &mut U> fn into_iter(self) -> IntoIter // IntoIter implements Iterator<Item = U> #}
この指針は全ての要素が同質であると意味付けられたコレクションに対して適用されます。
例えばstrはバイト列ですが、有効なUTF-8であるという保証があるため、この指針は適用できません。
従ってiter/iter_mut/into_iterといったメソッド群ではなく、
バイト列としてイテレートするstr::bytes、キャラクタ列としてイテレートするstr::charsを持ちます。
このガイドラインはメソッドにのみ適用され、関数は対象外です。
例えばurlクレートのpercent_encodeは文字列をパーセントエンコーディングしていくイテレータを返します。
この場合、iter/iter_mut/into_iterといった命名を使うことに利点はありません。
標準ライブラリでの例
イテレータの型名が、それを生成するメソッドと揃っている (C-ITER-TY)
into_iterというメソッドはIntoIterという型を返すべきです。
他のイテレータを返すメソッドでも同様です。
この指針は主にメソッドに対して適用されますが、関数に対しても大抵は適用できます。
例えばurlクレートのpercent_encode関数はPercentEncode
という型名のイテレータを返します。
このような命名法はvec::IntoIterのようにモジュール名を付けて呼ぶ際に最も有用です。
標準ライブラリでの例
Vec::iterはIterを返す。Vec::iter_mutはIterMutを返す。Vec::into_iterはIntoIterを返す。BTreeMap::keysはKeysを返す。BTreeMap::valuesはValuesを返す。
Featureの名前に余計な単語が入っていない (C-FEATURE)
Cargoのfeatureの名前に意味のない単語を付けないでください。
use-abcやwith-abcなどとせず、単にabcとするべきです。
標準ライブラリへの依存がオプションである場合が最もよく目にする例でしょう。 これを指針に従って行うと、以下のようになります。
# In Cargo.toml
[features]
default = ["std"]
std = []
# #![allow(unused_variables)] #fn main() { // In lib.rs #![cfg_attr(not(feature = "std"), no_std)] #}
このfeatureにstd以外の、use-stdやwith-stdあるいはその他の独創的な名前を付けないでください。
そうすることで、Cargoが暗黙的に追加するオプショナルな依存性のfeatureと沿った形になります。
例えばxというクレートがSerdeと標準ライブラリに対する依存をオプションとして持つとき、
[package]
name = "x"
version = "0.1.0"
[features]
std = ["serde/std"]
[dependencies]
serde = { version = "1.0", optional = true }
のようになります。そして、さらにxに依存するとき、Serdeへの依存を
features = ["serde"]で有効化できます。
また、同じように標準ライブラリへの依存をfeatures = ["std"]で有効化できます。
Cargoによって暗黙的に追加されるfeatureはserdeであり、use-serdeでもwith-serdeでもありません。
ですから、明示的なfeatureに対しても同じようにするべきなのです。
関連事項として、Cargoのfeatureは追加式ですから、
no-abcといったfeatureは大きな間違いです。
命名時に単語を並べる順番が揃っている (C-WORD-ORDER)
標準ライブラリにおけるエラー型をいくつか示します。
JoinPathsErrorParseBoolErrorParseCharErrorParseFloatErrorParseIntErrorRecvTimeoutErrorStripPrefixError
全て、動詞-オブジェクト-エラーの順番で並んでいます。
もし新たにアドレスのパースに失敗したことを表すエラーを追加するならば、
AddrParseError等ではなく、
一貫性を考えて動詞-オブジェクト-エラーの順に並べParseAddrErrorとすべきです。
どの順番を選ぶかは大して重要ではありませんが、クレート内での一貫性、 標準ライブラリの似た機能との整合性には注意してください。