项目
项目是指模块顶层允许的事物集合。然而,Rust 也允许某些项目出现在其他类型的项目中,例如在函数内部。无论是项目出现在模块级别还是其他项目内部,都适用相同的格式约定。
extern crate
语句必须位于文件的开头。它们必须按字母顺序排序。
use
语句和模块声明(mod foo;
,而不是 mod { ... }
)必须在其他项目之前。将导入放在模块声明之前。对每个声明进行版本排序,但 self
和 super
必须在任何其他名称之前。
不要自动移动用 #[macro_use]
注释的模块声明,因为这可能会改变语义。
函数定义
在 Rust 中,人们经常通过搜索 fn [函数名]
来查找函数,因此函数定义的格式必须能够实现这一点。
正确的顺序和间距是
#![allow(unused)] fn main() { [pub] [unsafe] [extern ["ABI"]] fn foo(arg1: i32, arg2: i32) -> i32 { ... } }
避免在签名本身内添加注释。
如果函数签名不适合在一行中显示,则在左括号后和右括号前断行,并将每个参数放在其自己的块缩进的行上。例如,
#![allow(unused)] fn main() { fn foo( arg1: i32, arg2: i32, ) -> i32 { ... } }
请注意最后一个参数上的尾随逗号。
元组和元组结构体
像编写函数参数列表一样编写类型列表。
像调用函数一样构建元组或元组结构体。
单行
#![allow(unused)] fn main() { struct Bar(Type1, Type2); let x = Bar(11, 22); let y = (11, 22, 33); }
枚举
在声明中,将每个变体放在单独的行上,并进行块缩进。
相应地格式化每个变体,可以是结构体(但没有 struct
关键字)、元组结构体或标识符(不需要特殊格式)。
#![allow(unused)] fn main() { enum FooBar { First(u32), Second, Error { err: Box<Error>, line: u32, }, } }
如果结构体变体是小的,则将其格式化为一行。在这种情况下,不要为字段列表使用尾随逗号,但要在每个大括号周围放置空格。
#![allow(unused)] fn main() { enum FooBar { Error { err: Box<Error>, line: u32 }, } }
在具有多个结构体变体的枚举中,如果任何结构体变体写在多行上,则对所有结构体变体使用多行格式。但是,这种情况可能表明您应该将变体的字段分解到它们自己的结构体中。
结构体和联合体
结构体名称与 struct
关键字在同一行,当它适合右边距时,左大括号也在同一行。所有结构体字段都缩进一次并以尾随逗号结尾。右大括号不缩进,并出现在其自己的行上。
#![allow(unused)] fn main() { struct Foo { a: A, b: B, } }
当且仅当字段的类型不适合右边距时,它会被拉到其自己的行并再次缩进。
#![allow(unused)] fn main() { struct Foo { a: A, long_name: LongType, } }
首选使用单元结构体(例如,struct Foo;
)而不是空结构体(例如,struct Foo();
或 struct Foo {}
,这些仅用于简化代码生成),但如果您必须使用空结构体,请将其保持在一行上,并且大括号之间没有空格:struct Foo;
或 struct Foo {}
。
相同的准则也适用于无标签联合声明。
#![allow(unused)] fn main() { union Foo { a: A, b: B, long_name: LongType, } }
元组结构体
如果可能,将整个结构体放在一行上。使用逗号和空格分隔括号内的类型。不要为单行元组结构体使用尾随逗号。不要在括号或分号周围放置空格。
#![allow(unused)] fn main() { pub struct Foo(String, u8); }
首选单元结构体而不是空元组结构体(这些仅用于简化代码生成),例如,struct Foo;
而不是 struct Foo();
。
对于多个字段(特别是如果元组结构体不适合在一行中显示),首选带有命名字段的适当结构体。
对于多行元组结构体,以块格式格式化字段,每个字段一行并带有尾随逗号。
#![allow(unused)] fn main() { pub struct Foo( String, u8, ); }
Trait
对 trait 项目使用块缩进。如果没有项目,则将 trait(包括其 {}
)格式化为单行。否则,在左大括号后和右大括号前断行。
#![allow(unused)] fn main() { trait Foo {} pub trait Bar { ... } }
如果 trait 有边界,则在冒号后放置一个空格,但在冒号前不放置空格,并在每个 +
周围放置空格,例如,
#![allow(unused)] fn main() { trait Foo: Debug + Bar {} }
如果可能,首选不在边界中换行(考虑使用 where
子句)。首选在边界之间断行,而不是在任何单个边界中断行。如果您必须断开边界,请将每个边界(包括第一个)放在其自己的块缩进的行上,在 +
之前断行,并将左大括号放在其自己的行上。
#![allow(unused)] fn main() { pub trait IndexRanges: Index<Range<usize>, Output=Self> + Index<RangeTo<usize>, Output=Self> + Index<RangeFrom<usize>, Output=Self> + Index<RangeFull, Output=Self> { ... } }
Impl
对 impl 项目使用块缩进。如果没有项目,则将 impl(包括其 {}
)格式化为单行。否则,在左大括号后和右大括号前断行。
#![allow(unused)] fn main() { impl Foo {} impl Bar for Foo { ... } }
如果可能,避免在签名中换行。如果非固有 impl 中需要换行,请在 for
之前立即断行,块缩进具体类型,并将左大括号放在其自己的行上。
#![allow(unused)] fn main() { impl Bar for Foo { ... } }
外部 crate
extern crate foo;
在关键字周围使用空格,在分号周围不使用空格。
模块
#![allow(unused)] fn main() { mod foo { } }
#![allow(unused)] fn main() { mod foo; }
在关键字周围和左大括号前使用空格,在分号周围不使用空格。
macro_rules!
对宏的完整定义使用 {}
。
#![allow(unused)] fn main() { macro_rules! foo { } }
泛型
首选将泛型子句放在一行上。断开项目声明的其他部分,而不是对泛型子句进行换行。如果泛型子句足够大以至于需要换行,则首选使用 where
子句代替。
不要在 <
前后或 >
前放置空格。仅在 >
后跟单词或左大括号而不是左括号时才放置空格。在每个逗号后放置一个空格。不要为单行泛型子句使用尾随逗号。
#![allow(unused)] fn main() { fn foo<T: Display, U: Debug>(x: Vec<T>, y: Vec<U>) ... impl<T: Display, U: Debug> SomeType<T, U> { ... }
如果泛型子句必须跨多行格式化,请将每个参数放在其自己的块缩进的行上,在左 <
后和右 >
前断行,并使用尾随逗号。
#![allow(unused)] fn main() { fn foo< T: Display, U: Debug, >(x: Vec<T>, y: Vec<U>) ... }
如果在泛型类型中绑定了关联类型,请在 =
周围放置空格。
#![allow(unused)] fn main() { <T: Example<Item = u32>> }
首选对泛型参数使用单字母名称。
where
子句
这些规则适用于任何项目上的 where
子句。
如果紧跟在任何类型的右括号之后,请在同一行上编写关键字 where
,并在其前面加一个空格。
否则,将 where
放在新行上,与相同的缩进级别对齐。将 where
子句的每个组件放在其自己的行上,并进行块缩进。使用尾随逗号,除非子句以分号终止。如果 where
子句后跟一个块(或赋值),请在新行上开始该块。示例
#![allow(unused)] fn main() { fn function<T, U>(args) where T: Bound, U: AnotherBound, { body } fn foo<T>( args ) -> ReturnType where T: Bound, { body } fn foo<T, U>( args, ) where T: Bound, U: AnotherBound, { body } fn foo<T, U>( args ) -> ReturnType where T: Bound, U: AnotherBound; // Note, no trailing comma. // Note that where clauses on `type` aliases are not enforced and should not // be used. type Foo<T> where T: Bound = Bar<T>; }
如果 where
子句非常短,则首选在类型参数上使用内联边界。
如果 where
子句的组件不适合并且包含 +
,则在每个 +
之前断开它,并块缩进延续行。将每个边界放在其自己的行上。例如,
#![allow(unused)] fn main() { impl<T: ?Sized, Idx> IndexRanges<Idx> for T where T: Index<Range<Idx>, Output = Self::Output> + Index<RangeTo<Idx>, Output = Self::Output> + Index<RangeFrom<Idx>, Output = Self::Output> + Index<RangeInclusive<Idx>, Output = Self::Output> + Index<RangeToInclusive<Idx>, Output = Self::Output> + Index<RangeFull>, }
类型别名
当类型别名适合时,将其保持在一行上。如果需要换行,请在 =
之前进行,并块缩进右侧。
#![allow(unused)] fn main() { pub type Foo = Bar<T>; // If multi-line is required type VeryLongType<T, U: SomeBound> = AnEvenLongerType<T, U, Foo<T>>; }
当类型后有尾随 where
子句,并且类型前没有 where
子句时,请在 =
之前断行并缩进。然后在 where
关键字之前断行,并正常格式化子句,例如,
#![allow(unused)] fn main() { // With only a trailing where clause type VeryLongType<T, U> = AnEvenLongerType<T, U, Foo<T>> where T: U::AnAssociatedType, U: SomeBound; }
当类型之前有 where
子句时,正常格式化它,并在最后一个子句后断行。不要在 =
之前缩进,使其在视觉上与之前的缩进子句区分开来。如果类型之后还有 where
子句,请在 where
关键字之前断行,并正常格式化子句。
#![allow(unused)] fn main() { // With only a preceding where clause. type WithPrecedingWC<T, U> where T: U::AnAssociatedType, U: SomeBound, = AnEvenLongerType<T, U, Foo<T>>; // Or with both a preceding and trailing where clause. type WithPrecedingWC<T, U> where T: U::AnAssociatedType, U: SomeBound, = AnEvenLongerType<T, U, Foo<T>> where T: U::AnAssociatedType2, U: SomeBound2; }
关联类型
像类型别名一样格式化关联类型。当关联类型具有边界时,在冒号后放置一个空格,但在冒号前不放置空格。
#![allow(unused)] fn main() { pub type Foo: Bar; }
外部项目
在编写外部项目(例如 extern "C" fn
)时,始终指定 ABI。例如,编写 extern "C" fn foo ...
或 unsafe extern "C" { ...}
,并避免 extern fn foo ...
和 unsafe extern { ... }
。
导入(use
语句)
如果可能,将导入格式化为一行。不要在大括号周围放置空格。
#![allow(unused)] fn main() { use a::b::c; use a::b::d::*; use a::b::{foo, bar, baz}; }
大型列表导入
首选使用多个导入而不是多行导入。但是,工具不应默认拆分导入。
如果导入确实需要多行(无论是由于单个名称列表不适合最大宽度,还是由于下面的嵌套导入规则),则在左大括号后和右大括号前断行,使用尾随逗号,并块缩进名称。
#![allow(unused)] fn main() { // Prefer foo::{long, list, of, imports}; foo::{more, imports}; // If necessary foo::{ long, list, of, imports, more, imports, // Note trailing comma }; }
导入的顺序
导入组是同一行或连续行上的一组导入。一个或多个空行或其他项目(例如,函数)分隔导入组。
在一个导入组内,导入必须按版本排序。导入组不得合并或重新排序。
例如,输入
#![allow(unused)] fn main() { use d; use c; use b; use a; }
输出
#![allow(unused)] fn main() { use c; use d; use a; use b; }
由于 macro_use
,属性也必须开始一个新组并防止重新排序。
排序列表导入
列表导入中的名称必须按版本排序,除非
self
和super
始终位于最前面(如果存在),并且- 组和 glob 导入始终位于最后(如果存在)。
这递归地适用。例如,a::*
在 b::a
之前,但 a::b
在 a::*
之前。例如,use foo::bar::{a, b::c, b::d, b::d::{x, y, z}, b::{self, r, s}};
。
规范化
工具必须进行以下规范化,递归地
use a::self;
->use a;
use a::{};
-> (无操作)use a::{b};
->use a::b;
工具不得以其他方式合并或取消合并导入列表或调整 glob 导入(没有显式选项)。
嵌套导入
如果列表导入中存在任何嵌套导入,则使用多行形式,即使导入适合在一行中显示。每个嵌套导入必须在其自己的行上,但非嵌套导入必须尽可能分组在较少的行上。
例如,
#![allow(unused)] fn main() { use a::b::{ x, y, z, u::{...}, w::{...}, }; }
合并/取消合并导入
一个例子
#![allow(unused)] fn main() { // Un-merged use a::b; use a::c::d; // Merged use a::{b, c::d}; }
工具默认情况下不得合并或取消合并导入。它们可以提供合并或取消合并作为选项。