- 代码风格
- 空白
- 空格
- 折行
- 对齐
- 避免块注释
- 文档注释
- 总结行
- 句子结构
- 避免文档内注释
- 解释上下文
- 开始的大括号总是出现的同一行。
match分支有大括号,除非是单行表达式。return语句有分号。- 行尾的逗号
- 一般命名约定
- 引用函数/方法名中的类型
- 避免冗余的前缀
- Getter/setter 方法
- 断言
- 导入
- 避免
use *,除非在测试里 - 当模块限定函数时,倾向于完全导入类型/traits。
- 在 crate 级重新导出最重要的类型。
- 类型和操作在一起定义。
- 空白
代码风格
空白
- 每行不能超出99个字符。
- 缩进只用空格,不用TAB。
- 行和文件末尾不要有空白。
空格
- 二元运算符左右加空格,包括属性里的等号:
#[deprecated = "Use `bar` instead."]fn foo(a: usize, b: usize) -> usize {a + b}
- 在分号和逗号后面加空格:
fn foo(a: Bar);MyStruct { foo: 3, bar: 4 }foo(bar, baz);
- 在单行语句块或
struct表达式的开始大括号之后和结束大括号之前加空格:
spawn(proc() { do_something(); })Point { x: 0.1, y: 0.3 }
折行
- 对于多行的函数签名,每个新行和第一个参数对齐。允许每行多个参数:
fn frobnicate(a: Bar, b: Bar,c: Bar, d: Bar)-> Bar {...}fn foo<T: This,U: That>(a: Bar,b: Bar)-> Baz {...}
- 多行函数调用一般遵循和签名统一的规则。然而,如果最后的参数开始了一个语句块,块的内容可以开始一个新行,缩进一层:
fn foo_bar(a: Bar, b: Bar,c: |Bar|) -> Bar {...}// 可以在同一行:foo_bar(x, y, |z| { z.transpose(y) });// 也可以在新一行缩进函数体:foo_bar(x, y, |z| {z.quux();z.rotate(x)})
对齐
常见代码不必在行中用多余的空格来对齐。
// 好struct Foo {short: f64,really_long: f64,}// 坏struct Bar {short: f64,really_long: f64,}// 好let a = 0;let radius = 7;// 坏let b = 0;let diameter = 7;
避免块注释
使用行注释:
// 等待主线程返回,并设置过程错误码// 明显地。
而不是:
/** 等待主线程返回,并设置过程错误码* 明显地。*/
文档注释
文档注释前面加三个斜线(///)而且提示你希望将注释包含在 Rustdoc 的输出里。
它们支持 Markdown 语言
而且是注释你的公开API的主要方式。
支持的 markdown 功能包括列在 GitHub Flavored Markdown 文档中的所有扩展,加上上角标。
总结行
任何文档注释中的第一行应该是一行总结代码的单行短句。该行用于在 Rustdoc 输出中的一个简短的总结性描述,所以,让它短比较好。
句子结构
所有的文档注释,包括总结行,一个以大写字母开始,以句号、问号,或者感叹号结束。最好使用完整的句子而不是片段。
总结行应该以 第三人称单数陈述句形式 来写。
基本上,这意味着用 “Returns” 而不是 “Return”。
例如:
/// 根据编译器提供的参数,设置一个缺省的运行时配置。////// 这个函数将阻塞直到整个 M:N 调度器池退出了。/// 这个函数也要求一个本地的线程可用。////// # 参数////// * `argc` 和 `argv` - 参数向量。在 Unix 系统上,该信息被`os::args`使用。////// * `main` - 运行在 M:N 调度器池内的初始过程。/// 一旦这个过程退出,调度池将开始关闭。/// 整个池(和这个函数)将只有在所有子线程完成执行后。////// # 返回值////// 返回值被用作进程返回码。成功是 0,101 是错误。
避免文档内注释
内嵌文档注释 只用于 注释 crates 和文件级的模块:
//! 核心库。//!//! 核心库是...
解释上下文
Rust 没有特定的构造器,只有返回新实例的函数。
这些在自动生成的类型文档中是不可见的,因此你应该专门链接到它们:
/// An iterator that yields `None` forever after the underlying iterator/// yields `None` once.////// These can be created through/// [`iter.fuse()`](trait.Iterator.html#method.fuse).pub struct Fuse<I> {// ...}
开始的大括号总是出现的同一行。
fn foo() {...}fn frobnicate(a: Bar, b: Bar,c: Bar, d: Bar)-> Bar {...}trait Bar {fn baz(&self);}impl Bar for Baz {fn baz(&self) {...}}frob(|x| {x.transpose()})
match 分支有大括号,除非是单行表达式。
match foo {bar => baz,quux => {do_something();do_something_else()}}
return 语句有分号。
fn foo() {do_something();if condition() {return;}do_something_else();}
行尾的逗号
Foo { bar: 0, baz: 1 }Foo {bar: 0,baz: 1,}match a_thing {None => 0,Some(x) => 1,}
一般命名约定
通常,Rust 倾向于为“类型级”结构(类型和 traits)使用 CamelCase 而为“值级”结构使用 snake_case 。更确切的约定:
| 条目 | 约定 |
|---|---|
| Crates | snake_case (但倾向于单个词) |
| Modules | snake_case |
| Types | CamelCase |
| Traits | CamelCase |
| Enum variants | CamelCase |
| Functions | snake_case |
| Methods | snake_case |
| General constructors | new 或 with_more_details |
| Conversion constructors | from_some_other_type |
| Local variables | snake_case |
| Static variables | SCREAMING_SNAKE_CASE |
| Constant variables | SCREAMING_SNAKE_CASE |
| Type parameters | 简洁 CamelCase,通常单个大写字母:T |
| Lifetimes | 短的小写: 'a |
在 CamelCase中, 首字母缩略词被当成一个单词:用 Uuid 而不是UUID。在 snake_case 中,首字母缩略词全部是小写: is_xid_start。
在 snake_case 或 SCREAMING_SNAKE_CASE 中,“单词”永远不应该只包含一个字母,
除非是最后一个“单词”。所以,我们有btree_map 而不是 b_tree_map,PI_2 而不是 PI2。
引用函数/方法名中的类型
函数名经常涉及类型名,最常见的约定例子像 as_slice。如果类型有一个纯粹的文本名字(忽略参数),
在类型约定和函数约定之间转换是直截了当的:
| 类型名 | 方法中的文本 |
|---|---|
String |
string |
Vec<T> |
vec |
YourType |
your_type |
涉及记号的类型遵循以下约定。这些规则有重叠;应用最适用的规则:
| 类型名 | 方法中的文本 |
|---|---|
&str |
str |
&[T] |
slice |
&mut [T] |
mut_slice |
&[u8] |
bytes |
&T |
ref |
&mut T |
mut |
*const T |
ptr |
*mut T |
mut_ptr |
避免冗余的前缀
一个模块中的条目的名字不应拿模块的名字做前缀:
倾向于
mod foo {pub struct Error { ... }}
而不是
mod foo {pub struct FooError { ... }}
这个约定避免了口吃(像 io::IoError)。库客户端可以在导入时重命名以避免冲突。
Getter/setter 方法
一些数据类型不希望提供对它们的域的直接访问,但是提供了 “getter” 和 “setter” 方法用于操纵域状态
(经常提供检查或其他功能)。
域 foo: T 的约定是:
- 方法
foo(&self) -> &T用于获得该域的当前值。 - 方法
set_foo(&self, val: T)用于设置域。(这里的val参数可能取&T或其他类型,取决于上下文。)
请注意,这个约定是关于通常数据类型的 getters/setters, 不是 关于构建者对象的。
断言
- 简单的布尔断言应该加上
is_或者其他的简短问题单词作为前缀,e.g.,is_empty。 - 常见的例外:
lt,gt,和其他已经确认的断言名。
导入
一个 crate/模块的导入应该按顺序包括下面各个部分,之间以空行分隔:
extern crate指令- 外部
use导入 - 本地
use导入 pub use导入
例如:
// Crates.extern crate getopts;extern crate mylib;// 标准库导入。use getopts::{optopt, getopts};use std::os;// 从一个我们写的库导入。use mylib::webserver;// 当我们导入这个模块时会被重新导出。pub use self::types::Webdata;
避免 use *,除非在测试里
Glob 导入有几个缺点:
- 更难知道名字在哪里绑定。
- 它们前向不兼容,因为新的上流导出可能与现存的名字冲突。
在写 test 子模块时,为方便导入 super::* 是合适的。
当模块限定函数时,倾向于完全导入类型/traits。
例如:
use option::Option;use mem;let i: isize = mem::transmute(Option(0));
在 crate 级重新导出最重要的类型。
Crates pub use 最常见的类型为方便,因此,客户端不必记住或写 crate 的模块结构以使用这些类型。
类型和操作在一起定义。
类型定义和使用它们的函数/模块应该在同一模块中定义,类型出现在函数/模块前面。
