Rust 语言风格指南
动机——为什么要使用格式化工具?
格式化代码是一项耗时耗力的机械性工作。如果使用自动格式化工具,开发者就可以从这项工作中解脱出来,专注于更重要的事情。
此外,通过坚持使用既定的风格指南(如本指南),开发者无需制定特别的风格规则,也无需与其他开发者争论应使用何种样式规则,从而节省了时间、沟通成本和精神耗损。
人类通过模式匹配来理解信息。通过确保所有 Rust 代码具有相似的格式,就能减少理解新项目所需的脑力劳动,从而降低新开发人员的入门门槛。
因此,使用格式化工具(如 rustfmt
)可以提高工作效率,而使用社区一致的格式规约(通常是使用格式化工具的默认设置)则会带来更大的好处。
默认 Rust 风格
《Rust 语言风格指南》定义了默认 Rust 风格,并建议开发者和工具遵循默认 Rust 样式。rustfmt
等工具使用此风格指南作为默认风格的参考。本风格指南中的所有内容,无论是否使用“必须”等语言或“插入空格......”或“在......后换行”等命令式语气,都是指默认样式。
这不应被解释为禁止开发人员遵循非默认样式,或禁止工具添加任何特定的配置选项。
格式约定
缩进和行宽
- 使用空格,而不是制表符。
- 每级缩进必须是 4 个空格(也就是说,字符串字面量和注释之外的所有缩进空格数都必须是 4 的倍数)。
- 一行的最大宽度为 100 个字符。
块缩进
与视觉化缩进(visual indent)相比,更倾向于分块缩进:
// 块缩进
a_function_call(
foo,
bar,
);
// 视觉化缩进
a_function_call(foo,
bar);
这样做的差异就会变小(例如,在上例中重命名了a_function_call
),向右移动的情况也会减少。
尾逗号
在任何类型的逗号分隔列表中,如果后面有换行符,请使用尾逗号:
function_call(
argument,
another_argument,
);
let array = [
element,
another_element,
yet_another_element,
];
这使得移动代码(例如通过复制和粘贴)变得更容易,并使差异更小,因为添加或删除项目不需要修改另一行来添加或删除逗号。
空行
不用空行或一个空行(即 1 或 2 个换行符)分隔程序项和语句。例如:
fn foo() {
let x = ...;
let y = ...;
let z = ...;
}
fn bar() {}
fn baz() {}
模块级别的程序序项
语句
表达式
类型
注释
以下关于注释的指导原则仅为建议,机器格式化工具可能会跳过注释格式化。
行注释 (//
) 优先于块注释 (/* ... */
)。
使用行注释时,在开头符号后留一个空格。
使用单行块注释时,在开头符号后和结尾符号前各留一个空格。对于多行块注释,在开头符号后加一个换行符,在结尾符号前加一个换行符。
注释最好独立成行。如果注释紧跟代码,则在注释前空格一个。如果块注释出现在行内,则使用周围的空格,就像使用标识符或关键字一样。不要在注释后或多行注释中任何一行的末尾使用拖尾空格。例如:
// 程序项中的注释。
struct Foo { ... }
fn foo() {} // 在一个项后的注释。
pub fn foo(/* 在参数前的注释 */ x: T) {...}
注释通常应是完整的句子。英文的注释开头用大写字母,结尾用句点(.
)(译注:若是使用中文注释,则换成中文标点符号)。内联块注释可视为不带标点符号的注释。
完全是注释的源文件行长度应限制在 80 个字符以内(包括注释符号,但不包括缩进),或该行的最大宽度(包括注释符号和缩进),以较小者为准:
// This comment goes up to the ................................. 80 char margin.
// 该注释的边距为 ............................. 80 字符。
{
// This comment is .............................................. 80 chars wide.
// 此注释宽 ............................................................. 80 字符。
}
{
{
{
{
{
{
// This comment is limited by the ......................... 100 char margin.
// 此注释受 ................................................ 100 字符边距的限制。
}
}
}
}
}
}
文档注释
优先使用行注释 (///
) 而不是块注释 (/** ... */
)。
优先使用外层文档注释(///
或 /** ... */
),仅使用内层文档注释(//!
和 /*! ... */
)编写模块级或 crate 块级的文档。
将文档注释放在属性之前。
属性
每个属性放在单独一行,跟程序项保持一致的缩进。如果是内部属性 (#!
),则缩进到程序项内部的位置。尽可能使用外属性。
对于带有参数列表的属性,格式应与函数类似。
#[repr(C)]
#[foo(foo, bar)]
#[long_multi_line_attribute(
split,
across,
lines,
)]
struct CRepr {
#![repr(C)]
x: f32,
y: f32,
}
对于带有等号的属性,在 =
前后各加一个空格,如 #[foo = 42]
。
必须只有一个 derive
属性。工具的作者们要注意:如果将多个 derive
属性合并为一个属性,通常必须保留派生名称的顺序,以保证正确性: #[derive(Foo)] #[derive(Bar)] struct Baz;
必须格式化为 #[derive(Foo, Bar)] struct Baz;
。
For attributes with an equal sign, put a single space before and after the =
,
e.g., #[foo = 42]
.
简短程序项
在本指南的许多地方,我们指定的格式取决于代码结构的简短。例如,单行结构文字与多行结构文字:
// 正常格式化
Foo {
f1: an_expression,
f2: another_expression(),
}
// “简短”格式化
Foo { f1, f2 }
我们让各个工具自行决定“简短”的确切含义。特别是,在不同的情况下,工具可以自由使用不同的定义。
一些合适的启发式方法是程序项的大小(以字符为单位)或程序项的复杂程度(例如,所有组件必须是简单的名称,而不是更复杂的子表达式)。有关合适的启发式方法的更多讨论,请参考此讨论问题。