项目

项目是指模块顶层允许的事物集合。然而,Rust 也允许某些项目出现在其他类型的项目中,例如在函数内部。无论是项目出现在模块级别还是其他项目内部,都适用相同的格式约定。

extern crate 语句必须位于文件的开头。它们必须按字母顺序排序。

use 语句和模块声明mod foo;,而不是 mod { ... })必须在其他项目之前。将导入放在模块声明之前。对每个声明进行版本排序,但 selfsuper 必须在任何其他名称之前。

不要自动移动用 #[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,属性也必须开始一个新组并防止重新排序。

排序列表导入

列表导入中的名称必须按版本排序,除非

  • selfsuper 始终位于最前面(如果存在),并且
  • 组和 glob 导入始终位于最后(如果存在)。

这递归地适用。例如,a::*b::a 之前,但 a::ba::* 之前。例如,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};
}

工具默认情况下不得合并或取消合并导入。它们可以提供合并或取消合并作为选项。