引言
6 分钟阅读
译文 · 基于 The Rust Style Guide
原文链接: https://doc.rust-lang.org/nightly/style-guide/index.html
引言
动机:为何使用格式化工具
格式化代码基本上是一项机械性工作,既耗费时间,也消耗心力。借助自动格式化工具,程序员可以从这项任务中解脱出来,把精力集中在更重要的事情上。
此外,遵循既定的风格指南(例如本指南),程序员就不必临时拟定风格规则,也不必与其他程序员争论该采用何种风格规则,从而节省时间、沟通成本和心力。
人类通过模式匹配来理解信息。确保所有 Rust 代码都采用相近的格式,理解新项目所需的心力就会减少,从而降低新开发者的入门门槛。
因此,使用格式化工具(例如 rustfmt)能带来生产力上的收益;而采用社区一致的格式(通常通过使用格式化工具的默认设置)收益更大。
默认的 Rust 风格
《Rust 风格指南》定义了默认的 Rust 风格,并建议开发者和工具遵循该默认风格。rustfmt 等工具以本风格指南作为默认风格的参考。本指南中的全部内容——无论是否使用「必须」之类的措辞,或「插入一个空格……」或「在……之后换行」这样的祈使语气——均指默认风格。
这不应被理解为禁止开发者采用非默认风格,或禁止工具添加任何特定的配置选项。
缺陷
若本风格指南与 rustfmt 不一致,可能是 rustfmt 的缺陷,也可能是风格指南的缺陷;无论哪种情况,请向 style 团队或 rustfmt 团队(或两者)报告,以便调查和修复。
若基于本风格指南和默认 Rust 风格实现新的格式化工具,请在现有的 Rust 代码语料上测试,并避免造成大范围破坏。此类工具的实现与测试可能会暴露风格指南或 rustfmt 中的缺陷,以及工具自身的缺陷。
我们通常以避免大范围破坏的方式来解决缺陷。
格式约定
缩进与行宽
- 使用空格,不要使用制表符。
- 每一级缩进必须为 4 个空格(即,字符串字面量和注释之外的所有缩进必须是 4 的倍数)。
- 一行的最大宽度为 100 个字符。
块缩进
优先使用块缩进,而非视觉缩进:
| |
这样可以得到更小的 diff(例如,若将上面示例中的 a_function_call 重命名),并减少向右漂移。
尾随逗号
在任何种类的逗号分隔列表中,若其后紧跟换行,则使用尾随逗号:
| |
这样更便于移动代码(例如通过复制粘贴),也能得到更小的 diff,因为追加或删除项时无需修改另一行来添加或删除逗号。
空行
项与语句之间用零行或一行空行分隔(即一个或两个换行)。例如:
| |
行尾空白
任何行的末尾都不要包含行尾空白。这包括空行、注释行、代码行和字符串字面量。
注意,在字符串字面量中避免行尾空白时需小心,以保持字面量的值不变。
排序
在多种情况下,默认的 Rust 风格会规定对某些内容进行排序。若未另行说明,此类排序应为「版本排序」,以确保(例如)x8 排在 x16 之前,尽管字符 1 排在字符 8 之前。
就 Rust 风格而言,要按版本排序比较两个字符串:
- 将两个字符串从头到尾处理为两列最大长度的块,其中每个块要么由非 ASCII 数字的字符序列组成,要么由 ASCII 数字序列组成(数字块),然后比较两字符串中对应的块。
- 比较两个数字块时,按数值比较,忽略前导零。若两块数值相等但前导数字个数不同,且这是这两字符串第一次出现此种情况,则将这两块视为相等(继续比较下一块),但记住哪一个字符串有更多的前导零。
- 若两块都不是数字块,则按 Unicode 字符的字典序比较,有两处例外:
_(下划线)紧排在(空格)之后、任何其他字符之前。(这将下划线视为词分隔符,与标识符中的常见用法一致。)- 除非另行说明,版本排序应将非小写字符(可出现在
UpperCamelCase标识符开头的字符)排在小写字符之前。
- 若比较进行到字符串末尾,且每一对块都被视为相等:
- 若某次数字比较记录了最早出现「一个字符串的前导零比另一个多」的位置,则将前导零更多的字符串排在前面。
- 否则,两字符串相等。
注意,存在多种被称为「版本排序」的算法,它们通常试图解决同一问题,但在诸多方面有所不同(例如对带前导零的数字的处理)。本算法并不声称精确匹配任何特定其他算法的行为,只求为 Rust 格式化产生简单且令人满意的结果。具体而言,本算法旨在:对前导零个数相同的一组符号产生令人满意的结果;对前导零个数不同的一组符号产生可接受且易于理解的结果。
例如,版本排序会将下列字符串按给定顺序排列:
_ZYXW_abcdA2ABCDZ_YXWZY_XWZY_XWZYXWZYXW_a1abcdu_zzzu8u16u32u64u128u256uausizeuzv000v00v0v0sv00tv0uv001v01v1v009v09v9v010v10w005s09tw5s009tx64x86x86_32x86_64x86_128x87zyxw
模块级项
语句
表达式
类型
注释
以下关于注释的指南仅为建议,机械格式化工具可能会跳过对注释的格式化。
优先使用行注释(//),而非块注释(/* ... */)。
使用行注释时,在起始标记之后放一个空格。
使用单行块注释时,在起始标记之后和结束标记之前各放一个空格。对于多行块注释,在起始标记之后换行,并在结束标记之前换行。
优先将注释单独成行。若注释跟在代码之后,在其前面放一个空格。若块注释以内联形式出现,则按标识符或关键字的方式处理其周围空白。
示例:
| |
注释通常应为完整句子。以大写字母开头,以句号(.)结尾。内联块注释可视为不带标点的备注。
整行都是注释的源码行,其长度应限制为 80 个字符(含注释标记,但不含缩进)或该行的最大宽度(含注释标记和缩进),取两者中较小者:
| |
文档注释
优先使用行注释(///),而非块注释(/** ... */)。
优先使用外部文档注释(/// 或 /** ... */),仅在编写模块级或 crate 级文档时使用内部文档注释(//! 和 /*! ... */)。
将文档注释放在属性之前。
属性
将每个属性单独成行,缩进到与项相同的层级。
对于内部属性(#!),缩进到项内部的层级。在可能的情况下,优先使用外部属性。
对于带参数列表的属性,按函数的方式格式化。
| |
对于带等号的属性,在 = 前后各放一个空格,例如 #[foo = 42]。
必须只有一个 derive 属性。供工具作者注意:若将多个 derive 属性合并为一个属性,为正确性起见,派生名称的顺序一般必须保留:#[derive(Foo)] #[derive(Bar)] struct Baz; 必须格式化为 #[derive(Foo, Bar)] struct Baz;。
小型 项
本指南多处规定的格式取决于某段代码构造是否小型。例如,单行与多行结构体字面量:
| |
我们把小型的确切含义留给各个工具自行决定。特别地,工具可以在不同情形下使用不同的定义。
一些合适的启发式包括项的大小(以字符计)或项的复杂度(例如,所有组成部分必须是简单名称,而非更复杂的子表达式)。关于合适启发式的更多讨论,见该 issue。