来源:[规范]https://rust-coding-guidelines.github.io/rust-coding-guidelines-zh/overview.html
一、命名
1.1 在一个crate
中,保证标识符的命名规则使用统一词序
词并不重要,而是保证词序的一致性。即使是类似的功能。
例如:JoinPathsError
ParseBoolError
ParseCharError
ParseFloatError
ParseIntError
RecvTimeoutError
StripPrefixError
上面为动词-宾语-error
的顺序命名。如果想新增错误类型,应该是ParseAddrError
而不是AddrParseError
。
1.2 为crate feature
命名时不应有无意义的词占用
比如使用abc命名来替代 use-abc 或 with-abc。并且 Cargo 要求 features 应该是相互叠加的,所以像 no-abc 这种负向的 feature 命名实际上并不正确。[features]
// 不符合
default = ["use-std"]
std = []
// 不符合
no-abc=[]
1.3 标识符命名应该符合阅读习惯
命名保证简洁明了,容易理解,增加代码的高可读性。
一些好的实践包括但不限于:
- 使用正确的英文单词并符合英文语法,不要使用拼音
- 仅使用常见或领域内通用的单词缩写
- 布尔型变量或函数避免使用否定形式,双重否定不利于理解
- 不要使用 Unicode 标识符
|
1.4 作用域越大命名越精确,反之应简短
- 对于全局函数、全局变量、宏、类型名、枚举命名,应当精确描述并全局唯一。
- 对于函数局部变量,或者结构体、枚举中的成员变量,在其命名能够准确表达含义的前提下,应该尽量简短,避免冗余信息重复描述。
|
1.5 用于访问或获取数据的 getter 类方法通常不要使用 get_ 前缀
因为 Rust 所有权语义的存在,此例子中两个方法的参数分别是共享引用 &self
和独占引用 &mut self
,分别代表了 getter 的语义。
也存在一些例外情况可以用 get_
前缀。pub struct First;
pub struct Second;
pub struct S {
first: First,
second: Second,
}
impl S {
// 不符合:访问成员函数名字不用get_前缀。
pub fn get_first(&self) -> &First {
&self.first
}
// 不符合:
// 同样不建议 `get_mut_first`, or `mut_first`.
pub fn get_first_mut(&mut self) -> &mut First {
&mut self.first
}
// set_前缀是可以的
pub fn set_first(&mut self, f: First) -> &mut First {
self.first = f;
}
}
正例pub struct First;
pub struct Second;
pub struct S {
first: First,
second: Second,
}
impl S {
// 符合
pub fn first(&self) -> &First {
&self.first
}
// 符合
pub fn first_mut(&mut self) -> &mut First {
&mut self.first
}
// set_前缀是可以的
pub fn set_first(&mut self, f: First) {
self.first = f;
}
}
例外
只有当需要显式的语义来通过getter
获取某种数据,才会使用get
命名。例如,Cell::get
可以访问一个Cell
的内容。
对于进行运行时验证的getter,例如边界检查,可以考虑添加一个 Unsafe 的_unchecked
配套方法。// 进行一些运行时验证,例如边界检查
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;
特别是rust
语法中的getter
和类型转换区别很小,有些时候不是很清晰。
1.6 遵循 iter/ iter_mut/ into_iter 规范来生成迭代器
对于容纳 U
类型的容器 ,其迭代器方法应该遵循iter/ iter_mut/ into_iter
这三种命名方式。
返回的迭代器类型名称也应该和其方法名保持一致,如一个叫做into_iter()
的方法应该返回一个叫做IntoIter
的类型。
例外
标准库中存在一个例外: str
类型是有效 UTF-8 字节的切片(slice),概念上与同质集合略有差别,所以 str
没有提供 iter/iter_mut/into_iter
命名的迭代器方法,而是提供 str::bytes
方法来输出字节迭代器、 str::chars
方法来输出字符迭代器。
1.7 避免使用语言内置保留字、关键字、内置类型和trait等特殊名称
命名必须要避免使用语言内置的保留字、关键字、内置类型和trait等特殊名称。
1.8 避免在变量的命名中添加类型标识
不需要在变量命名中添加关于类型的标识。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`
1.9 定义全局静态变量时需加前缀XXX以便和常量有所区分
为了提升代码可读性和可维护性,有必要将常量的命名和全局静态变量加以区分。static G_EVENT: [i32;5]=[1,2,3,4,5];
const MAGIC_NUM: i32 = 65 ;
1.10 使用统一的命名风格
Rust 倾向于在类型级的结构中使用驼峰命名风格,在 变量、值(实例)、函数名等结构中使用蛇形命名风格。
很多语言都会提供代码格式化工具和 linter 来消灭这类缺陷。Rust 有内置的 cargo fmt
和 cargo clippy
来帮助开发者统一代码风格,来避免常见的开发错误。
1.10.5 变量命名规范
Rust变量名可以由字母,数字或者下划线组成。同时还有以下3个限制条件:
- 不能以数字开头
- 字母区分大小写
- 不能只有下划线
1.10.1 蛇形命名法
- 文件名:例如:hello_world.rs、main_xxx.rs
- 变量名:例如:zhangsan_name
- 函数名/方法名:例如:func_name()
- 模块名:例如:test_tokio {}
- 宏
- 特性:Features
1.10.2 大驼峰命名法
- 结构体:例如:struct ExampleStruct { name: String}
- enum类型:例如:enum IpAddress {IPV4(u8,u8,u8,u8)}
- Trait类型:例如:trait Summer {}
- type类型:例如:type SummerInt = u8
1.10.3 其它
- 关联常量:全部大写,例如:NAME、AGE
- 全局变量:全部大写
- 语句:跟C,Java语言等一样,每行语句结束都要添加
;
- 通用构造函数:new 或者 init
- 转换构造函数:from_some_other_type
- 生存期:简短的字符串即可,尽量保持语义
- 不建议以
-rs
或_rs
为后缀来命名包名
1.10.4 说明
在大驼峰命名法下,缩略语、复合词算作一个单词,如应该使用 Uuid 而非 UUID
,使用 Usize
而不是 USize
,或者是 Stdin
而不是 StdIn
。
在蛇形命名、常量命名下,每个词不应该由单个单词组成。比如,使用 btree_map
而不使用 b_tree_map
,使用 PI_2
而不使用 PI2
。
1.11 类型转换函数命名需要遵循所有权语义
进行特定类型转换的方法名应该包含以下前缀:
名称前缀 | 内存代价 | 所有权 |
---|---|---|
as_ |
无代价 | borrowed -> borrowed |
to_ |
代价昂贵 | borrowed -> borrowed、borrowed -> owned (非 Copy 类型)、owned -> owned (Copy 类型) |
into_ |
看情况 | owned -> owned (非 Copy 类型) |
以 as_
和 into_
作为前缀的类型转换通常是降低抽象层次 ,要么是查看背后的数据(as) ,要么是分解(deconstructe)背后的数据(into)。相对来说,以 to_
作为前缀的类型转换处于同一个抽象层次,但是底层会做更多工作,比如多了内存拷贝等操作。
如:当一个类型用更高级别的语义封装一个内部类型时,应该使用 into_inner()
方法名来取出被封装类型的值。
如果类型转换方法返回的类型具有 mut
修饰,那么这个方法的名称应如同返回类型组成部分的顺序那样,带有 mut
。如 Vec::as_mut_slice
返回 &mut [T]
类型,这个方法的功能正如其名称所述,所以这个名称优于 as_slice_mut
。
1.12 过程宏的包名规范
TODO
二、注释
注释分为两类:
- 普通注释使用
//
或/* ... */
。 - 文档注释使用
///
、//!
或/** ... **/
。
描述注释则为普通注释,描述文档则为文档注释。
2.1 文档要干练简洁
注释固然重要,但是代码其实就是文档本身。高可读的类型名、函数名和变量名, 要远胜过要用注释解释的含糊不清的名字。
不要描述显然易见的代码。
注释内容始终围绕两个关键点来构建:
- What : 用于阐述代码为了什么而实现。
- how : 用于阐述代码如何去使用。
Rust 项目文档应该始终基于 rustdoc 工具来构建,rustdoc 支持 Markdown 格式,为了使得文档更加美观易读,文档注释应该使用 Markdown 格式。
模块级文档://!
普通文档注释示例:///
2.2 注释应该有宽度限制
2.3 使用行注释而避免使用块注释
尽量使用//
或///
,而非块注释。
对于文档注释,仅在编写模块级文档时使用 //!,在其他情况使用 ///更好。
2.4 文件头注释包含版权说明
文件头(即,模块级)注释应先包含版权说明。如果文件头注释需要增加其他内容,可以在版权说明下面补充。
可以包括
- 文件功能说明。
- 作者。
- 创建日期 和 最后修改日期。
- 注意事项。
- 开源许可证(比如, Apache 2.0, BSD, LGPL, GPL)。
- 其他。
2.5 在注释中使用 TODO 来帮助任务协作
使用rust的TODO
来完成协作开发。
2.6 在公开的返回Result类型的函数文档中增加 Error 注释
在pub
的返回Result
类型的函数文档中,建议增加 # Error
注释来解释什么场景下该函数会返回什么样的错误类型,方便用户处理错误。#![warn(clippy::missing_errors_doc)]
fn main() {
use std::io;
// 符合:增加了规范的 Errors 文档注释
/// # Errors
///
/// Will return `Err` if `filename` does not exist or the user does not have
/// permission to read it.
pub fn read(filename: String) -> io::Result<String> {
unimplemented!();
}
2.7 如果公开的API在某些情况下会发生Panic,则相应文档中需增加 Panic 注释
在pub
函数文档中,建议增加 # Panic
注释来解释该函数在什么条件下会 Panic
,便于使用者进行预处理。#![warn(clippy::missing_panics_doc)]
// 符合:增加了规范的 Panic 注释
/// # Panics
///
/// Will panic if y is 0
pub fn divide_by(x: i32, y: i32) -> i32 {
if y == 0 {
panic!("Cannot divide by 0")
} else {
x / y
}
}
2.8 在文档注释中要使用空格代替 tab
提倡使用四个空格代替tab,在文档注释中也应该统一使用四个空格。
END 附录
END.1 开发环境
- Visual Studio Code
- CLion或者IDEA(这里需要注意版本,因为低版本可能会导致无法安装到最新的rust语法插件)
- …
END.2 语法提示工具
- Rust Analyzer
- Rust Language Server(rls)
目前推荐Rust Analyzer
。
END.3 工具链安装
推荐使用rustup
。
END.4 Rust版本
TODO
END.5 包管理
Cargo 是 Rust 项目必不可少的包管理器。
Cargo 通过 Cargo.toml 配置文件来管理 crate。
END.6 常用Cargo插件
END.6.1 Clippy
Clippy 是一个静态分析工具,它提供了很多检查,比如错误、 样式、 性能问题、 Unsafe UB问题等等。从1.29版本开始,Clippy可以用于 Stable Rust中。
END.6.2 Rustfmt
Rustfmt 是一个根据风格指南原则来格式化代码的工具。
END.6.3 Rustfix
从 Rust 2018 edition开始,Rustfix就被包含在 Rust 中。它可以用来修复编译器警告。
END.6.4 Cargo Edit
Cargo Edit插件为Cargo扩展了三个命令:
- Cargo add,在命令行增加新的依赖,而不需要去知道这个依赖的语义版本。
- Cargo rm,在命令行删除一个指定依赖。
- Cargo upgrade,在命令行升级一个指定依赖。
END.6.5 Cargo Deny
该插件可以检测依赖中的软件许可证(License),如果和开发者配置的不符合,则会拒绝使用该依赖。
END.6.6 Crago Make
Rust任务运行器和构建工具。
END.7 测试
TODO
END.8 术语解释
TODO
END.9 推荐优化
TODO
END.10 使用的 crate 及版本
- tokio:网络及高性能IO
- futures:异步
- serde:序列化
- syn:对 TokenStream 解析的库
- quote:把代码转换成可以操作的数据
- anyhow/thierr:错误处理