12 KiB
命名规范
基本的Rust命名规范在RFC 430中有描述.
通常,对于"type-level"的构造Rust倾向于使用驼峰命名,而对于'value-level'的构造使用蛇形命名。详情如下:
条目 | 惯例 |
---|---|
包Crates | unclear |
模块Modules | snake_case |
类型Types | UpperCamelCase |
特征Traits | UpperCamelCase |
枚举变量Enum variants | 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 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写的,因此这种多余的注释其实没有任何意义。
类型转换要遵守as_
, to_
, into_
命名惯例(C-CONV)
类型转换应该通过方法调用的方式实现,其中的前缀规则如下:
方法前缀 | 性能开销 | 所有权改变 |
---|---|---|
as_ |
Free | borrowed -> borrowed |
to_ |
Expensive | borrowed -> borrowed borrowed -> owned (non-Copy types) owned -> owned (Copy types) |
into_ |
Variable | owned -> owned (non-Copy types) |
For example:
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>
当一个单独的值被某个类型所包装时,访问该类型的内部值应通过into_inner()
方法来访问。例如将一个缓冲区值包装为BufReader
类型,还有GzDecoder
、AtomicBool
等,都是这种类型。
如果mut
限定符在返回类型中出现,那么在命名上也应该体现出来。例如,Vec::as_mut_slice
就说明它返回了一个mut切片,在这种情况下as_mut_slice
比as_slice_mut
更适合。
// 返回类型是一个mut切片.
fn as_mut_slice(&mut self) -> &mut [T];
标准库中的一些例子
读访问器(Getter)的名称遵循Rust的命名规范(C-GETTER)
除了少数例外,在Rust代码中get
前缀不用于getter。
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
中的内容。
有些getter会在过程中执行运行时检查,那么我们就可以考虑添加_unchecked
getter函数,这个函数虽然不安全,但是往往具有更高的性能,
典型的例子如下:
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;
标准库示例
std::io::Cursor::get_mut
std::ptr::Unique::get_mut
std::sync::PoisonError::get_mut
std::sync::atomic::AtomicBool::get_mut
std::collections::hash_map::OccupiedEntry::get_mut
<[T]>::get_unchecked
一个集合上的方法,如果返回迭代器,需遵循命名规则:iter
, iter_mut
, into_iter
(C-ITER)
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
类型是一个utf8字节数组切片,与同构性集合有一点微妙的差别,它可以认为是字节集合,也可以认为是字符集合,因此它提供了str::bytes
去遍历字节,还有str::chars
去遍历字符,而并没有直接定义iter
等方法。
上述规则只适用于方法,并不适用于函数。例如url
包的percent_encode
函数返回一个迭代器用于遍历百分比编码(Percent encoding)的字符串片段. 在这种情况下,使用iter
/iter_mut
/into_iter
诸如此类的函数命名无法表达任何具体的含义。
标准库示例
迭代器的类型应该与产生它的方法名相匹配(C-ITER-TY)
例如形如into_iter()
的方法应该返回一个IntoIter
类型,与之相似,其它任何返回迭代器的方法也应该遵循这种命名惯例。
上述规则主要应用于方法,但是经常对于函数也适用。例如上文提到的url包中的percent_encode
函数,返回了一个[PercentEncode
]类型.
特别是,当这些类型跟包名前缀一起使用时,将具备非常清晰的含义,例如vec::IntoIter
.
标准库示例
Vec::iter
returnsIter
Vec::iter_mut
returnsIterMut
Vec::into_iter
returnsIntoIter
BTreeMap::keys
returnsKeys
BTreeMap::values
returnsValues
Cargo Feature的名称不应该包含占位词(C-FEATURE)
不要在Cargo feature中包含无法传达任何意义的词,例如use-abc
或with-abc
,直接命名为abc
即可。
一个典型的例子就是:一个包对标准库有可选性的依赖。标准的写法如下:
# 在Cargo.toml中
[features]
default = ["std"]
std = []
// 在我们自定义的lib.rs中
#![cfg_attr(not(feature = "std"), no_std)]
除了std
之外,不要使用任何ust-std
或者with-std
等自以为很有创造性的名称。
命名要使用一致性的词序(C-WORD-ORDER)
这是一些标准库中的错误类型:
JoinPathsError
ParseBoolError
ParseCharError
ParseFloatError
ParseIntError
RecvTimeoutError
StripPrefixError
它们都使用了谓语-宾语-错误
的词序,如果我们想要表达一个网络地址无法分析的错误,由于词序一致性的原则,命名应该如下ParseAddrError
,而不是AddrParseError
。
词序和个人习惯有很大关系,想要注意的是,你可以选择合适的词序,但是要在包的范畴内保持一致性,就如标准库中的包一样。