命名
使用统一的命名风格
Rust 倾向于在“类型”级的结构中使用大驼峰(UpperCamelCase) 命名风格,在 “变量、值(实例)、函数名”等结构中使用蛇形(snake_case)命名风格。
下面是汇总信息:
| Item | 规范 |
|---|---|
| 包(Crates) | 通常使用 snake_case |
| 模块(Modules) | snake_case |
| 类型(Types) | UpperCamelCase |
| 特质(Traits) | UpperCamelCase |
| 枚举体(Enum variants) | UpperCamelCase |
| 函数(Functions) | snake_case |
| 方法(Methods) | snake_case |
| 通用构造函数(General constructors) | new 或者 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) | 简短的 lowercase,通常使用单个小写字母 'a, 'de, 'src,尽量保持语义 |
| 特性(Features) | snake_case |
说明 :
- 在
UpperCamelCase情况下,由首字母缩写组成的缩略语和 复合词的缩写,算作单个词。比如,应该使用Uuid而非UUID,使用Usize而不是USize,或者是Stdin而不是StdIn。 - 在
snake_case中,首字母缩写和缩略词是小写的is_xid_start。 - 在
snake_case或者SCREAMING_SNAKE_CASE情况下,每个词不应该由单个字母组成——除非这个字母是最后一个词。比如,使用btree_map而不使用b_tree_map,使用PI_2而不使用PI2。
关于包命名:
- 由于历史问题,包名有两种形式
snake_case或kebab-case,但实际在代码中需要引入包名的时候,Rust 只能识别snake_case,也会自动将kebab-case识别为kebab_case。所以建议使用snake_case。 - Crate 的名称通常不应该使用
-rs或者-rust作为后缀或者前缀。但是有些情况下,比如是其他语言移植的同名 Rust 实现,则可以使用-rs后缀来表明这是 Rust 实现的版本。
Rust 命名规范在 RFC 0430 中有也描述。
Lint 检测
| lint name | Clippy 可检测 | Rustc 可检测 | Lint Group |
|---|---|---|---|
Rustc: non_camel_case_types | no | yes | Style |
Rustc: non_snake_case | no | yes | Style |
类型转换函数命名需要遵循所有权语义
进行特定类型转换的方法名应该包含以下前缀:
| 名称前缀 | 内存代价 | 所有权 |
|---|---|---|
as_ | 无代价 | borrowed -> borrowed |
to_ | 代价昂贵 | borrowed -> borrowed borrowed -> owned (非 Copy 类型) owned -> owned (Copy 类型) |
into_ | 看情况 | owned -> owned (非 Copy 类型) |
以 as_ 和 into_ 作为前缀的类型转换通常是 降低抽象层次 ,要么是查看背后的数据 ( as ) ,要么是分解 (deconstructe) 背后的数据 ( into ) 。
相对来说,以 to_ 作为前缀的类型转换处于同一个抽象层次,但是底层会做更多工作,比如多了内存拷贝等操作。
当一个类型用更高级别的语义 (higher-level semantics) 封装 (wraps) 一个内部类型时,应该使用 into_inner() 方法名来取出被封装类型的值。
同一个crate中标识符的命名规则应该使用统一的词序
具体选择什么样的词序并不重要,但务必要保证同一个 crate 内词序的一致性。 若提供与标准库中相似功能的东西时,也要与标准库名称的词性顺序一致.
拿错误类型来举个例子:
当crate中类型名称都按照 动词-宾语-error 这样的顺序来命名错误类型时,如果要增加新的错误类型,则也需要按同样的词序来增加。
struct AddrParseError {}// 不符合:与标准库错误类型词序 "`动-宾-Error`" 不一致,应该为 `ParseAddrError`
struct ParseAddrError{} // 符合: 与标准库错误类型一致
标识符命名应该符合阅读习惯
一些好的实践包括但不限于:
- 使用正确的英文单词并符合英文语法,不要使用拼音
- 仅使用常见或领域内通用的单词缩写
- 布尔型变量或函数避免使用否定形式,双重否定不利于理解
- 不要使用
Unicode标识符
作用域越大命名越精确,反之应简短
- 对于全局函数、全局变量、宏、类型名、枚举命名,应当精确描述并全局唯一。
- 对于函数局部变量,或者结构体、枚举中的成员变量,在其命名能够准确表达含义的前提下,应该尽量简短,避免冗余信息重复描述。
// 不符合:描述不精确
static GET_COUNT: i32 = 42;
// 不符合:信息冗余
enum WebEvent {
PageLoadEvent,
PageUnloadEvent,
KeyPressEvent(char),
PasteEvent(String),
ClickEvent { x: i64, y: i64 },
}
// 不符合:信息冗余
type MaskSize = u16;
pub struct HeaderMap {
mask: MaskSize,
}
#![allow(unused)]
fn main() {
// 符合
static MAX_THREAD_COUNT: i32 = 42;
// 符合: 上下文信息已经知道它是 Event
enum WebEvent {
PageLoad,
PageUnload,
KeyPress(char),
Paste(String),
Click { x: i64, y: i64 },
}
// 符合:在使用它的地方自然就知道是描述谁的大小
type Size = u16;
pub struct HeaderMap {
mask: Size,
}
}
避免使用语言内置保留字、关键字、内置类型和trait等特殊名称
命名必须要避免使用语言内置的保留字、关键字、内置类型和trait等特殊名称。
具体可以参考The Rust Reference-Keywords。
避免在变量的命名中添加类型标识
因为Rust语言类型系统崇尚显式的哲学,所以不需要在变量命名中也添加关于类型的标识。
#![allow(unused)]
fn main() {
let account_bytes: Vec<u8> = read_some_input(); // 不符合:account 的类型很清楚,没必要在命名中加 `_bytes`
let account_str = String::from_utf8(account_bytes)?; // 不符合:account 的类型很清楚,没必要在命名中加 `_str`
let account: Account = account_str.parse()?; // 不符合:account 的类型很清楚,没必要在命名中加 `_str`
}
定义全局静态变量时需加前缀G_以便和常量有所区分
为了提升代码可读性和可维护性,有必要将常量的命名和全局静态变量加以区分。所以在定义全局静态变量时,需要以前缀G_命名。
// 不符合: 无法通过命名直接区分常量和静态变量
static EVENT: [i32;5]=[1,2,3,4,5];
const MAGIC_NUM: i32 = 65 ;
// 符合: 通过前缀区分常量和静态变量
static G_EVENT: [i32;5]=[1,2,3,4,5];
const MAGIC_NUM: i32 = 65 ;