|
|
# 命名规范
|
|
|
|
|
|
基本的 Rust 命名规范在 [RFC 430] 中有描述。
|
|
|
|
|
|
通常,对于 **type-level** 的构造 Rust 倾向于使用**驼峰命名法**,而对于 **value-level** 的构造使用**蛇形命名法**。详情如下:
|
|
|
|
|
|
| 条目 | 惯例 |
|
|
|
| ---- | ---------- |
|
|
|
| 包 Crates | [unclear](https://github.com/rust-lang/api-guidelines/issues/29) |
|
|
|
| 模块 Modules | `snake_case` |
|
|
|
| 类型 Types | `UpperCamelCase` |
|
|
|
| 特征 Traits | `UpperCamelCase` |
|
|
|
| 枚举 Emumerations | `UpperCamelCase` |
|
|
|
| 结构体 Structs | `UpperCamelCase` |
|
|
|
| 函数 Functions | `snake_case` |
|
|
|
| 方法 Methods | `snake_case` |
|
|
|
| 通用构造器 General constructors | `new` or `with_more_details` |
|
|
|
| 转换构造器 Conversion constructors | `from_some_other_type` |
|
|
|
| 宏 Macros | `snake_case!` |
|
|
|
| 局部变量 Local variables | `snake_case` |
|
|
|
| 静态类型 Statics | `SCREAMING_SNAKE_CASE` |
|
|
|
| 常量 Constants | `SCREAMING_SNAKE_CASE` |
|
|
|
| 类型参数 Type parameters | `UpperCamelCase`,通常使用一个大写字母: `T` |
|
|
|
| 生命周期 Lifetimes | 通常使用小写字母: `'a`,`'de`,`'src` |
|
|
|
| Features | [unclear](https://github.com/rust-lang/api-guidelines/issues/101) but see [C-FEATURE] |
|
|
|
|
|
|
对于**驼峰命名法**,复合词的缩略形式我们认为是一个单独的词语,所以**只对首字母进行大写**:使用 `Uuid` 而不是 ~~`UUID`~~,`Usize` 而不是 ~~`USize`~~,`Stdin` 而不是 ~~`StdIn`~~。
|
|
|
|
|
|
对于**蛇形命名法**,缩略词用全小写:`is_xid_start`。
|
|
|
|
|
|
对于**蛇形命名法**(包括全大写的 `SCREAMING_SNAKE_CASE`),除了最后一部分,其它部分的词语都不能由单个字母组成:
|
|
|
`btree_map` 而不是 ~~`b_tree_map`~~,`PI_2` 而不是 ~~`PI2`~~.
|
|
|
|
|
|
包名**不应该**使用 `-rs` 或者 `-rust` 作为后缀,因为每一个包都是 Rust 写的,因此这种多余的注释其实没有任何意义。
|
|
|
|
|
|
[RFC 430]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md
|
|
|
[C-FEATURE]: #c-feature
|
|
|
|
|
|
## 特征命名
|
|
|
特征的名称应该使用动词,而不是形容词或者名词,例如 `Print` 和 `Draw` 明显好于 `Printable` 和 `Drawable`。
|
|
|
|
|
|
## 类型转换要遵守 `as_`,`to_`,`into_` 命名惯例(C-CONV)
|
|
|
类型转换应该通过方法调用的方式实现,其中的前缀规则如下:
|
|
|
|
|
|
| 方法前缀 | 性能开销 | 所有权改变 |
|
|
|
| ------ | ---- | --------- |
|
|
|
| `as_` | Free | borrowed -\> borrowed |
|
|
|
| `to_` | Expensive | borrowed -\> borrowed<br>borrowed -\> owned (non-Copy types)<br>owned -\> owned (Copy types) |
|
|
|
| `into_` | Variable | owned -\> owned (non-Copy types) |
|
|
|
|
|
|
例如:
|
|
|
|
|
|
- [`str::as_bytes()`] 把 `str` 变成 UTF-8 字节数组,性能开销是 0。输入是一个借用的 `&str`,输出也是一个借用的 `&str`
|
|
|
- [`Path::to_str`] 会执行一次昂贵的 UTF-8 字节数组检查,输入和输出都是借用的。对于这种情况,如果把方法命名为 `as_str` 是不正确的,因为这个方法的开销还挺大
|
|
|
- [`str::to_lowercase()`] 在调用过程中会遍历字符串的字符,且可能会分配新的内存对象。输入是一个借用的 `str`,输出是一个有独立所有权的 `String`
|
|
|
- [`String::into_bytes()`] 返回 `String` 底层的 `Vec<u8>` 数组,转换本身是零消耗的。该方法获取 `String` 的所有权,然后返回一个新的有独立所有权的 `Vec<u8>`
|
|
|
|
|
|
|
|
|
[`str::as_bytes()`]: https://doc.rust-lang.org/std/primitive.str.html#method.as_bytes
|
|
|
[`Path::to_str`]: https://doc.rust-lang.org/std/path/struct.Path.html#method.to_str
|
|
|
[`str::to_lowercase()`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase
|
|
|
[`f64::to_radians()`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_radians
|
|
|
[`String::into_bytes()`]: https://doc.rust-lang.org/std/string/struct.String.html#method.into_bytes
|
|
|
[`BufReader::into_inner()`]: https://doc.rust-lang.org/std/io/struct.BufReader.html#method.into_inner
|
|
|
[`BufWriter::into_inner()`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html#method.into_inner
|
|
|
|
|
|
|
|
|
当一个单独的值被某个类型所包装时,访问该类型的内部值应通过 `into_inner()` 方法来访问。例如将一个缓冲区值包装为 [`BufReader`] 类型,还有 [`GzDecoder`]、[`AtomicBool`] 等,都是这种类型。
|
|
|
|
|
|
|
|
|
[`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html#method.into_inner
|
|
|
[`GzDecoder`]: https://docs.rs/flate2/0.2.19/flate2/read/struct.GzDecoder.html#method.into_inner
|
|
|
[`AtomicBool`]: https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.into_inner
|
|
|
|
|
|
如果 `mut` 限定符在返回类型中出现,那么在命名上也**应该**体现出来。例如,[`Vec::as_mut_slice`] 就说明它返回了一个 `mut` 切片,在这种情况下 `as_mut_slice` 比 `as_slice_mut` 更适合。
|
|
|
|
|
|
[`Vec::as_mut_slice`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_mut_slice
|
|
|
|
|
|
```rust
|
|
|
// 返回类型是一个 `mut` 切片
|
|
|
fn as_mut_slice(&mut self) -> &mut [T];
|
|
|
```
|
|
|
|
|
|
### 标准库中的一些例子
|
|
|
|
|
|
- [`Result::as_ref`](https://doc.rust-lang.org/std/result/enum.Result.html#method.as_ref)
|
|
|
- [`RefCell::as_ptr`](https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.as_ptr)
|
|
|
- [`slice::to_vec`](https://doc.rust-lang.org/std/primitive.slice.html#method.to_vec)
|
|
|
- [`Option::into_iter`](https://doc.rust-lang.org/std/option/enum.Option.html#method.into_iter)
|
|
|
|
|
|
|
|
|
## 读访问器(Getter)的名称遵循 Rust 的命名规范(C-GETTER)
|
|
|
|
|
|
除了少数例外,在 Rust代码中 `get` 前缀不用于 Getter。
|
|
|
|
|
|
```rust
|
|
|
pub struct S {
|
|
|
first: First,
|
|
|
second: Second,
|
|
|
}
|
|
|
|
|
|
impl S {
|
|
|
// 而不是 get_first
|
|
|
pub fn first(&self) -> &First {
|
|
|
&self.first
|
|
|
}
|
|
|
|
|
|
// 而不是 get_first_mut,get_mut_first,or mut_first
|
|
|
pub fn first_mut(&mut self) -> &mut First {
|
|
|
&mut self.first
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
至于上文提到的少数例外,如下:**当有且仅有一个值**能被 Getter 所获取时,才使用 `get` 前缀。例如,[`Cell::get`] 能直接访问到 `Cell` 中的内容。
|
|
|
|
|
|
[`Cell::get`]: https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get
|
|
|
|
|
|
有些 Getter 会在过程中执行运行时检查,那么我们就可以考虑添加 `_unchecked` Getter 函数,这个函数虽然不安全,但是往往具有更高的性能。
|
|
|
典型的例子如下:
|
|
|
|
|
|
```rust
|
|
|
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;
|
|
|
```
|
|
|
|
|
|
[`TempDir::path`]: https://docs.rs/tempdir/0.3.5/tempdir/struct.TempDir.html#method.path
|
|
|
[`TempDir::into_path`]: https://docs.rs/tempdir/0.3.5/tempdir/struct.TempDir.html#method.into_path
|
|
|
|
|
|
### 标准库示例
|
|
|
|
|
|
- [`std::io::Cursor::get_mut`](https://doc.rust-lang.org/std/io/struct.Cursor.html#method.get_mut)
|
|
|
- [`std::ptr::Unique::get_mut`](https://doc.rust-lang.org/std/ptr/struct.Unique.html#method.get_mut)
|
|
|
- [`std::sync::PoisonError::get_mut`](https://doc.rust-lang.org/std/sync/struct.PoisonError.html#method.get_mut)
|
|
|
- [`std::sync::atomic::AtomicBool::get_mut`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.get_mut)
|
|
|
- [`std::collections::hash_map::OccupiedEntry::get_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.OccupiedEntry.html#method.get_mut)
|
|
|
- [`<[T]>::get_unchecked`](https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked)
|
|
|
|
|
|
## 一个集合上的方法,如果返回迭代器,需遵循命名规则:`iter`,`iter_mut`,`into_iter` (C-ITER)
|
|
|
|
|
|
```rust
|
|
|
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 字节数组切片,与同构性集合有一点微妙的差别,它可以认为是字节集合,也可以认为是字符集合,因此它提供了 [`str::bytes`] 去遍历字节,还有 [`str::chars`] 去遍历字符,而并没有直接定义 `iter` 等方法。
|
|
|
|
|
|
[`str::bytes`]: https://doc.rust-lang.org/std/primitive.str.html#method.bytes
|
|
|
[`str::chars`]: https://doc.rust-lang.org/std/primitive.str.html#method.chars
|
|
|
|
|
|
上述规则只适用于方法,并不适用于函数。例如 `url` 包的 [`percent_encode`] 函数返回一个迭代器用于遍历百分比编码([Percent encoding](https://en.wikipedia.org/wiki/Percent-encoding))的字符串片段. 在这种情况下,使用 `iter`/`iter_mut`/`into_iter` 诸如此类的函数命名无法表达任何具体的含义。
|
|
|
|
|
|
[`percent_encode`]: https://docs.rs/url/1.4.0/url/percent_encoding/fn.percent_encode.html
|
|
|
[RFC 199]: https://github.com/rust-lang/rfcs/blob/master/text/0199-ownership-variants.md
|
|
|
|
|
|
### 标准库示例
|
|
|
|
|
|
- [`Vec::iter`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter)
|
|
|
- [`Vec::iter_mut`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter_mut)
|
|
|
- [`Vec::into_iter`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_iter)
|
|
|
- [`BTreeMap::iter`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.iter)
|
|
|
- [`BTreeMap::iter_mut`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.iter_mut)
|
|
|
|
|
|
## 迭代器的类型应该与产生它的方法名相匹配(C-ITER-TY)
|
|
|
例如形如 `into_iter()` 的方法应该返回一个 `IntoIter` 类型,与之相似,其它任何返回迭代器的方法也应该遵循这种命名惯例。
|
|
|
|
|
|
上述规则主要应用于方法,但是经常对于函数也适用。例如上文提到的 `url` 包中的 [`percent_encode`] 函数,返回了一个 [`PercentEncode`] 类型。
|
|
|
|
|
|
[PercentEncode]: https://docs.rs/url/1.4.0/url/percent_encoding/struct.PercentEncode.html
|
|
|
|
|
|
特别是,当这些类型跟包名前缀一起使用时,将具备非常清晰的含义,例如 [`vec::IntoIter`]。
|
|
|
|
|
|
[`vec::IntoIter`]: https://doc.rust-lang.org/std/vec/struct.IntoIter.html
|
|
|
|
|
|
### 标准库示例
|
|
|
|
|
|
* [`Vec::iter`] returns [`Iter`][slice::Iter]
|
|
|
* [`Vec::iter_mut`] returns [`IterMut`][slice::IterMut]
|
|
|
* [`Vec::into_iter`] returns [`IntoIter`][vec::IntoIter]
|
|
|
* [`BTreeMap::keys`] returns [`Keys`][btree_map::Keys]
|
|
|
* [`BTreeMap::values`] returns [`Values`][btree_map::Values]
|
|
|
|
|
|
[`Vec::iter`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter
|
|
|
[slice::Iter]: https://doc.rust-lang.org/std/slice/struct.Iter.html
|
|
|
[`Vec::iter_mut`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter_mut
|
|
|
[slice::IterMut]: https://doc.rust-lang.org/std/slice/struct.IterMut.html
|
|
|
[`Vec::into_iter`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_iter
|
|
|
[vec::IntoIter]: https://doc.rust-lang.org/std/vec/struct.IntoIter.html
|
|
|
[`BTreeMap::keys`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.keys
|
|
|
[btree_map::Keys]: https://doc.rust-lang.org/std/collections/btree_map/struct.Keys.html
|
|
|
[`BTreeMap::values`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.values
|
|
|
[btree_map::Values]: https://doc.rust-lang.org/std/collections/btree_map/struct.Values.html
|
|
|
|
|
|
|
|
|
<a id="c-feature"></a>
|
|
|
## Cargo Feature 的名称不应该包含占位词(C-FEATURE)
|
|
|
|
|
|
不要在 [Cargo feature] 中包含无法传达任何意义的词,例如 `use-abc` 或 `with-abc`,直接命名为 `abc` 即可。
|
|
|
|
|
|
[Cargo feature]: http://doc.crates.io/manifest.html#the-features-section
|
|
|
|
|
|
一个典型的例子就是:一个包对标准库有可选性的依赖。标准的写法如下:
|
|
|
|
|
|
```toml
|
|
|
# 在 Cargo.toml 中
|
|
|
|
|
|
[features]
|
|
|
default = ["std"]
|
|
|
std = []
|
|
|
```
|
|
|
|
|
|
```rust
|
|
|
// 在我们自定义的 lib.rs 中
|
|
|
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
```
|
|
|
除了 `std` 之外,不要使用任何 `ust-std` 或者 `with-std` 等自以为很有创造性的名称。
|
|
|
|
|
|
## 命名要使用一致性的词序(C-WORD-ORDER)
|
|
|
|
|
|
这是一些标准库中的错误类型:
|
|
|
|
|
|
- [`JoinPathsError`](https://doc.rust-lang.org/std/env/struct.JoinPathsError.html)
|
|
|
- [`ParseBoolError`](https://doc.rust-lang.org/std/str/struct.ParseBoolError.html)
|
|
|
- [`ParseCharError`](https://doc.rust-lang.org/std/char/struct.ParseCharError.html)
|
|
|
- [`ParseFloatError`](https://doc.rust-lang.org/std/num/struct.ParseFloatError.html)
|
|
|
- [`ParseIntError`](https://doc.rust-lang.org/std/num/struct.ParseIntError.html)
|
|
|
- [`RecvTimeoutError`](https://doc.rust-lang.org/std/sync/mpsc/enum.RecvTimeoutError.html)
|
|
|
- [`StripPrefixError`](https://doc.rust-lang.org/std/path/struct.StripPrefixError.html)
|
|
|
|
|
|
它们都使用了 `谓语-宾语-错误` 的词序,如果我们想要表达一个网络地址无法分析的错误,由于词序一致性的原则,命名应该如下 `ParseAddrError`,而不是 `AddrParseError`。
|
|
|
|
|
|
词序和个人习惯有很大关系,想要注意的是,你可以选择合适的词序,但是要在包的范畴内保持一致性,就如标准库中的包一样。
|