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 }

我们让各个工具自行决定“简短”的确切含义。特别是,在不同的情况下,工具可以自由使用不同的定义。

一些合适的启发式方法是程序项的大小(以字符为单位)或程序项的复杂程度(例如,所有组件必须是简单的名称,而不是更复杂的子表达式)。有关合适的启发式方法的更多讨论,请参考此讨论问题

非格式化约定

Cargo.toml 的约定

决定这些准则的原则

程序项

程序项(item,简称项)包括模块顶层允许使用的一系列内容。不过,Rust 也允许某些程序项出现在其他类型的程序项中,如函数中。无论程序项是出现在模块层还是出现在其他程序项中,都适用相同的格式约定。

extern crate 语句必须放在文件的首位。它们必须按字母顺序排列。

use 语句和模块声明(mod foo;,而不是 mod { ... })必须放在其他程序项之前。将导入放在模块声明之前。按字母顺序排序,但 selfsuper 必须排在其他名称之前。

不要自动移动注有 #[macro_use] 的模块声明,因为这可能会改变语义。

函数定义

在 Rust 中,人们经常通过搜索 fn [function-name] 来查找函数,因此函数定义的格式必须能够满足这一点。

正确的排序和空格位置是:

[pub] [unsafe] [extern ["ABI"]] fn foo(arg1: i32, arg2: i32) -> i32 {
    ...
}

避免在签名本身中添加注释。

如果函数签名不能放在一行内,则在开头括号后和结尾括号前分隔,并将每个参数放在自己的缩进行内。例如:

fn foo(
    arg1: i32,
    arg2: i32,
) -> i32 {
    ...
}

注意最后一个参数后面的逗号。

元组和元组结构

像编写函数的参数列表一样编写类型列表。

像调用函数一样构建元组或元组结构体。

单行

struct Bar(Type1, Type2);

let x = Bar(11, 22);
let y = (11, 22, 33);

Enums

In the declaration, put each variant on its own line, block indented.

Format each variant accordingly as either a struct (but without the struct keyword), a tuple struct, or an identifier (which doesn't require special formatting):

enum FooBar {
    First(u32),
    Second,
    Error {
        err: Box<Error>,
        line: u32,
    },
}

If a struct variant is small, format it on one line. In this case, do not use a trailing comma for the field list, but do put spaces around each brace:

enum FooBar {
    Error { err: Box<Error>, line: u32 },
}

In an enum with multiple struct variants, if any struct variant is written on multiple lines, use the multi-line formatting for all struct variants. However, such a situation might be an indication that you should factor out the fields of the variant into their own struct.

Structs and Unions

Struct names follow on the same line as the struct keyword, with the opening brace on the same line when it fits within the right margin. All struct fields are indented once and end with a trailing comma. The closing brace is not indented and appears on its own line.

struct Foo {
    a: A,
    b: B,
}

If and only if the type of a field does not fit within the right margin, it is pulled down to its own line and indented again.

struct Foo {
    a: A,
    long_name:
        LongType,
}

Prefer using a unit struct (e.g., struct Foo;) to an empty struct (e.g., struct Foo(); or struct Foo {}, these only exist to simplify code generation), but if you must use an empty struct, keep it on one line with no space between the braces: struct Foo; or struct Foo {}.

The same guidelines are used for untagged union declarations.

union Foo {
    a: A,
    b: B,
    long_name:
        LongType,
}

Tuple structs

Put the whole struct on one line if possible. Separate types within the parentheses using a comma and space. Don't use a trailing comma for a single-line tuple struct. Don't put spaces around the parentheses or semicolon:

pub struct Foo(String, u8);

Prefer unit structs to empty tuple structs (these only exist to simplify code generation), e.g., struct Foo; rather than struct Foo();.

For more than a few fields (in particular if the tuple struct does not fit on one line), prefer a proper struct with named fields.

For a multi-line tuple struct, block-format the fields with a field on each line and a trailing comma:

pub struct Foo(
    String,
    u8,
);

Traits

Use block-indent for trait items. If there are no items, format the trait (including its {}) on a single line. Otherwise, break after the opening brace and before the closing brace:

trait Foo {}

pub trait Bar {
    ...
}

If the trait has bounds, put a space after the colon but not before, and put spaces around each +, e.g.,

trait Foo: Debug + Bar {}

Prefer not to line-break in the bounds if possible (consider using a where clause). Prefer to break between bounds than to break any individual bound. If you must break the bounds, put each bound (including the first) on its own block-indented line, break before the + and put the opening brace on its own line:

pub trait IndexRanges:
    Index<Range<usize>, Output=Self>
    + Index<RangeTo<usize>, Output=Self>
    + Index<RangeFrom<usize>, Output=Self>
    + Index<RangeFull, Output=Self>
{
    ...
}

Impls

Use block-indent for impl items. If there are no items, format the impl (including its {}) on a single line. Otherwise, break after the opening brace and before the closing brace:

impl Foo {}

impl Bar for Foo {
    ...
}

Avoid line-breaking in the signature if possible. If a line break is required in a non-inherent impl, break immediately before for, block indent the concrete type and put the opening brace on its own line:

impl Bar
    for Foo
{
    ...
}

Extern crate

extern crate foo;

Use spaces around keywords, no spaces around the semicolon.

Modules

mod foo {
}
mod foo;

Use spaces around keywords and before the opening brace, no spaces around the semicolon.

macro_rules!

Use {} for the full definition of the macro.

macro_rules! foo {
}

Generics

Prefer to put a generics clause on one line. Break other parts of an item declaration rather than line-breaking a generics clause. If a generics clause is large enough to require line-breaking, prefer a where clause instead.

Do not put spaces before or after < nor before >. Only put a space after > if it is followed by a word or opening brace, not an opening parenthesis. Put a space after each comma. Do not use a trailing comma for a single-line generics clause.

fn foo<T: Display, U: Debug>(x: Vec<T>, y: Vec<U>) ...

impl<T: Display, U: Debug> SomeType<T, U> { ...

If the generics clause must be formatted across multiple lines, put each parameter on its own block-indented line, break after the opening < and before the closing >, and use a trailing comma.

fn foo<
    T: Display,
    U: Debug,
>(x: Vec<T>, y: Vec<U>) ...

If an associated type is bound in a generic type, put spaces around the =:

<T: Example<Item = u32>>

Prefer to use single-letter names for generic parameters.

where clauses

These rules apply for where clauses on any item.

If immediately following a closing bracket of any kind, write the keyword where on the same line, with a space before it.

Otherwise, put where on a new line at the same indentation level. Put each component of a where clause on its own line, block-indented. Use a trailing comma, unless the clause is terminated with a semicolon. If the where clause is followed by a block (or assignment), start that block on a new line. Examples:

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>;

If a where clause is very short, prefer using an inline bound on the type parameter.

If a component of a where clause does not fit and contains +, break it before each + and block-indent the continuation lines. Put each bound on its own line. E.g.,

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>,

Type aliases

Keep type aliases on one line when they fit. If necessary to break the line, do so after the =, and block-indent the right-hand side:

pub type Foo = Bar<T>;

// If multi-line is required
type VeryLongType<T, U: SomeBound> =
    AnEvenLongerType<T, U, Foo<T>>;

Where possible avoid where clauses and keep type constraints inline. Where that is not possible split the line before and after the where clause (and split the where clause as normal), e.g.,

type VeryLongType<T, U>
where
    T: U::AnAssociatedType,
    U: SomeBound,
= AnEvenLongerType<T, U, Foo<T>>;

Associated types

Format associated types like type aliases. Where an associated type has a bound, put a space after the colon but not before:

pub type Foo: Bar;

extern items

When writing extern items (such as extern "C" fn), always specify the ABI. For example, write extern "C" fn foo ..., not extern fn foo ..., or extern "C" { ... }.

Imports (use statements)

Format imports on one line where possible. Don't put spaces around braces.

use a::b::c;
use a::b::d::*;
use a::b::{foo, bar, baz};

Large list imports

Prefer to use multiple imports rather than a multi-line import. However, tools should not split imports by default.

If an import does require multiple lines (either because a list of single names does not fit within the max width, or because of the rules for nested imports below), then break after the opening brace and before the closing brace, use a trailing comma, and block indent the names.

// Prefer
foo::{long, list, of, imports};
foo::{more, imports};

// If necessary
foo::{
    long, list, of, imports, more,
    imports,  // Note trailing comma
};

Ordering of imports

A group of imports is a set of imports on the same or sequential lines. One or more blank lines or other items (e.g., a function) separate groups of imports.

Within a group of imports, imports must be sorted ASCIIbetically (uppercase before lowercase). Groups of imports must not be merged or re-ordered.

E.g., input:

use d;
use c;

use b;
use a;

output:

use c;
use d;

use a;
use b;

Because of macro_use, attributes must also start a new group and prevent re-ordering.

Ordering list import

Names in a list import must be sorted ASCIIbetically, but with self and super first, and groups and glob imports last. This applies recursively. For example, a::* comes before b::a but a::b comes before a::*. E.g., use foo::bar::{a, b::c, b::d, b::d::{x, y, z}, b::{self, r, s}};.

Normalisation

Tools must make the following normalisations, recursively:

  • use a::self; -> use a;
  • use a::{}; -> (nothing)
  • use a::{b}; -> use a::b;

Tools must not otherwise merge or un-merge import lists or adjust glob imports (without an explicit option).

Nested imports

If there are any nested imports in a list import, then use the multi-line form, even if the import fits on one line. Each nested import must be on its own line, but non-nested imports must be grouped on as few lines as possible.

For example,

use a::b::{
    x, y, z,
    u::{...},
    w::{...},
};

Merging/un-merging imports

An example:

// Un-merged
use a::b;
use a::c::d;

// Merged
use a::{b, c::d};

Tools must not merge or un-merge imports by default. They may offer merging or un-merging as an option.

语句

let 语句

: 后面和 = 的两边(若它们存在的话)空一格。分号前不要空格。

// 一条注释。
let pattern: Type = expr;

let pattern;
let pattern: Type;
let pattern = expr;

如果可能,将声明格式化成一行。如果不可能,则在 = 之后尝试分割,如果声明适合在两行中进行。将表达式块缩进。

let pattern: Type =
    expr;

如果第一行仍不能排在一行上,则在 : 之后分行,并使用块缩进。即使在 : 后分行后类型还需要多行,也应将第一行放在与 : 相同的行上,并遵守合并规则

let pattern:
    Type =
    expr;

例如:

let Foo {
    f: abcd,
    g: qwer,
}: Foo<Bar> =
    Foo { f, g };

let (abcd,
    defg):
    Baz =
{ ... }

如果表达式包含多行,若表达式的第一行适合在余下空位上,则表达式与 = 保留在同一行,表达式的其余部分不再缩进。如果第一行不合适,则将表达式放在后面的行中,分块缩进。如果表达式是一个代码块,且类型或模式覆盖多行,则将开头括号放在新的一行,且不缩进(这样可以将代码块内部与类型分开);否则,开头括号放在 = 之后。

示例:

let foo = Foo {
    f: abcd,
    g: qwer,
};

let foo =
    ALongName {
        f: abcd,
        g: qwer,
    };

let foo: Type = {
    an_expression();
    ...
};

let foo:
    ALongType =
{
    an_expression();
    ...
};

let Foo {
    f: abcd,
    g: qwer,
}: Foo<Bar> = Foo {
    f: blimblimblim,
    g: blamblamblam,
};

let Foo {
    f: abcd,
    g: qwer,
}: Foo<Bar> = foo(
    blimblimblim,
    blamblamblam,
);

else 块(let-else 语句)

一个 let 语句可以包含一个 else 组件,使其成为一个 let-else 语句。在这种情况下,应始终对 else 块前面的组件(即 let pattern: Type = initializer_expr 部分)采用与其他 let 语句相同的格式化规则。

如果以下条件都符合,则将整个 let-else 语句格式化为一行:

  • 整个语句很
  • else 块只包含一个单行表达式,不包含任何语句
  • else 块不包含注释
  • else 块前面的 let 语句组件可以格式化为单行
let Some(1) = opt else { return };

否则,let-else 语句需要换行。

如果将 let-else 语句换成多行,切勿在else{ 之间换行,一定要在 } 之前换行。

如果 else 前面的 let 语句组件可以格式化为一行,但 let-else 不符合完全放在一行的条件,则应将 else { 放在初始化表达式的同一行,并在两者之间留一个空格,然后在 { 之后换行。缩进结尾的 } 以匹配 let,并将包含的代码块再缩进一步。

let Some(1) = opt else {
    return;
};

let Some(1) = opt else {
    // nope
    return
};

如果 else 前面的 let 语句组件可以在一行中格式化,但 else { 不能在同一行中格式化时,则在 else 之前换行。

    let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name
    else {
        return;
    };

如果初始化表达式为多行,则在且仅在以下所有条件都符合的情况下,将 else 关键字和代码块的开头括号(即 else {)放在与初始化表达式结尾相同的行上,并在它们之间留一个空格:

  • 初始化表达式以一个或多个结束括号、方括号和/或大括号结束
  • 该行没有其他内容
  • 该行的缩进级别与初始 let 关键字的缩进级别相同

例如:

let Some(x) = y.foo(
    "abc",
    fairly_long_identifier,
    "def",
    "123456",
    "string",
    "cheese",
) else {
    bar()
}

否则,将 else 关键字和开头括号放在初始化表达式结束后的下一行,else 关键字的缩进级别与 let 关键字的缩进级别相同。

例如:

fn main() {
    let Some(x) = abcdef()
        .foo(
            "abc",
            some_really_really_really_long_ident,
            "ident",
            "123456",
        )
        .bar()
        .baz()
        .qux("fffffffffffffffff")
    else {
        return
    };

    let Some(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) =
        bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    else {
        return;
    };

    let LongStructName(AnotherStruct {
        multi,
        line,
        pattern,
    }) = slice.as_ref()
    else {
        return;
    };

    let LongStructName(AnotherStruct {
        multi,
        line,
        pattern,
    }) = multi_line_function_call(
        arg1,
        arg2,
        arg3,
        arg4,
    ) else {
        return;
    };
}

在语句位置使用宏

在语句位置使用宏时,使用圆括号或方括号作为分隔符,并以分号结束。请勿在名称、!、分隔符或 ; 前后使用空格。

// 注释
a_macro!(...);

语句位置中的表达式

表达式和分号之间不要加空格。

<expr>;

用分号结束语句位置上的所有表达式,除非这些表达式以块结束或用作块的值。

例如:

{
    an_expression();
    expr_as_value()
}

return foo();

loop {
    break;
}

表达式为空类型时,即使可以传递,也要使用分号。例如:

fn foo() { ... }

fn bar() {
    foo();
}

表达式

块表达式必须在首行 { 后和末行 } 前加换行符,除非根据其他样式规则可以写成单行。

代码块前的关键字(如 unsafeasync)必须与开头的括号在同一行,关键字与开头的括号之间用一个空格隔开。缩进代码块的内容。

fn block_as_stmt() {
    a_call();

    {
        a_call_inside_a_block();

        // a comment in a block
        the_value
    }
}

fn block_as_expr() {
    let foo = {
        a_call_inside_a_block();

        // a comment in a block
        the_value
    };
}

fn unsafe_block_as_stmt() {
    a_call();

    unsafe {
        a_call_inside_a_block();

        // a comment in a block
        the_value
    }
}

If a block has an attribute, put it on its own line before the block:

fn block_as_stmt() {
    #[an_attribute]
    {
        #![an_inner_attribute]

        // a comment in a block
        the_value
    }
}

Avoid writing comments on the same lines as either of the braces.

Write an empty block as {}.

Write a block on a single line if:

  • it is either used in expression position (not statement position) or is an unsafe block in statement position,
  • it contains a single-line expression and no statements, and
  • it contains no comments

For a single-line block, put spaces after the opening brace and before the closing brace.

Examples:

fn main() {
    // Single line
    let _ = { a_call() };
    let _ = unsafe { a_call() };

    // Not allowed on one line
    // Statement position.
    {
        a_call()
    }

    // Contains a statement
    let _ = {
        a_call();
    };
    unsafe {
        a_call();
    }

    // Contains a comment
    let _ = {
        // A comment
    };
    let _ = {
        // A comment
        a_call()
    };

    // Multiple lines
    let _ = {
        a_call();
        another_call()
    };
    let _ = {
        a_call(
            an_argument,
            another_arg,
        )
    };
}

Closures

Don't put any extra spaces before the first | (unless the closure is prefixed by a keyword such as move); put a space between the second | and the expression of the closure. Between the |s, use function definition syntax, but elide types where possible.

Use closures without the enclosing {}, if possible. Add the {} when you have a return type, when there are statements, when there are comments inside the closure, or when the body expression is a control-flow expression that spans multiple lines. If using braces, follow the rules above for blocks. Examples:

|arg1, arg2| expr

move |arg1: i32, arg2: i32| -> i32 {
    expr1;
    expr2
}

|| Foo {
    field1,
    field2: 0,
}

|| {
    if true {
        blah
    } else {
        boo
    }
}

|x| unsafe {
    expr
}

Struct literals

If a struct literal is small, format it on a single line, and do not use a trailing comma. If not, split it across multiple lines, with each field on its own block-indented line, and use a trailing comma.

For each field: value entry, put a space after the colon only.

Put a space before the opening brace. In the single-line form, put spaces after the opening brace and before the closing brace.

Foo { field1, field2: 0 }
let f = Foo {
    field1,
    field2: an_expr,
};

Functional record update syntax is treated like a field, but it must never have a trailing comma. Do not put a space after ...

let f = Foo {
    field1,
    ..an_expr
};

Tuple literals

Use a single-line form where possible. Do not put spaces between the opening parenthesis and the first element, or between the last element and the closing parenthesis. Separate elements with a comma followed by a space.

Where a single-line form is not possible, write the tuple across multiple lines, with each element of the tuple on its own block-indented line, and use a trailing comma.

(a, b, c)

let x = (
    a_long_expr,
    another_very_long_expr,
);

Tuple struct literals

Do not put space between the identifier and the opening parenthesis. Otherwise, follow the rules for tuple literals:

Foo(a, b, c)

let x = Foo(
    a_long_expr,
    another_very_long_expr,
);

Enum literals

Follow the formatting rules for the various struct literals. Prefer using the name of the enum as a qualifying name, unless the enum is in the prelude:

Foo::Bar(a, b)
Foo::Baz {
    field1,
    field2: 1001,
}
Ok(an_expr)

Array literals

Write small array literals on a single line. Do not put spaces between the opening square bracket and the first element, or between the last element and the closing square bracket. Separate elements with a comma followed by a space.

If using the repeating initializer, put a space after the semicolon only.

Apply the same rules if using vec! or similar array-like macros; always use square brackets with such macros. Examples:

fn main() {
    let x = [1, 2, 3];
    let y = vec![a, b, c, d];
    let a = [42; 10];
}

For arrays that have to be broken across lines, if using the repeating initializer, break after the ;, not before. Otherwise, follow the rules below for function calls. In any case, block-indent the contents of the initializer, and put line breaks after the opening square bracket and before the closing square bracket:

fn main() {
    [
        a_long_expression();
        1234567890
    ]
    let x = [
        an_expression,
        another_expression,
        a_third_expression,
    ];
}

Array accesses, indexing, and slicing.

Don't put spaces around the square brackets. Avoid breaking lines if possible. Never break a line between the target expression and the opening square bracket. If the indexing expression must be broken onto a subsequent line, or spans multiple lines itself, then block-indent the indexing expression, and put newlines after the opening square bracket and before the closing square bracket:

Examples:

fn main() {
    foo[42];
    &foo[..10];
    bar[0..100];
    foo[4 + 5 / bar];
    a_long_target[
        a_long_indexing_expression
    ];
}

Unary operations

Do not include a space between a unary op and its operand (i.e., !x, not ! x). However, there must be a space after &mut. Avoid line-breaking between a unary operator and its operand.

Binary operations

Do include spaces around binary ops (i.e., x + 1, not x+1) (including = and other assignment operators such as += or *=).

For comparison operators, because for T op U, &T op &U is also implemented: if you have t: &T, and u: U, prefer *t op u to t op &u. In general, within expressions, prefer dereferencing to taking references, unless necessary (e.g. to avoid an unnecessarily expensive operation).

Use parentheses liberally; do not necessarily elide them due to precedence. Tools should not automatically insert or remove parentheses. Do not use spaces to indicate precedence.

If line-breaking, block-indent each subsequent line. For assignment operators, break after the operator; for all other operators, put the operator on the subsequent line. Put each sub-expression on its own line:

foo_bar
    + bar
    + baz
    + qux
    + whatever

Prefer line-breaking at an assignment operator (either = or +=, etc.) rather than at other binary operators.

Control flow

Do not include extraneous parentheses for if and while expressions.

if true {
}

is better than

if (true) {
}

Do include extraneous parentheses if it makes an arithmetic or logic expression easier to understand ((x * 15) + (y * 20) is fine)

Function calls

Do not put a space between the function name, and the opening parenthesis.

Do not put a space between an argument, and the comma which follows.

Do put a space between an argument, and the comma which precedes it.

Prefer not to break a line in the callee expression.

Single-line calls

Do not put a space between the function name and open paren, between the open paren and the first argument, or between the last argument and the close paren.

Do not put a comma after the last argument.

foo(x, y, z)

Multi-line calls

If the function call is not small, it would otherwise over-run the max width, or any argument or the callee is multi-line, then format the call across multiple lines. In this case, put each argument on its own block-indented line, break after the opening parenthesis and before the closing parenthesis, and use a trailing comma:

a_function_call(
    arg1,
    a_nested_call(a, b),
)

Method calls

Follow the function rules for calling.

Do not put any spaces around the ..

x.foo().bar().baz(x, y, z);

Macro uses

If a macro can be parsed like other constructs, format it like those constructs. For example, a macro use foo!(a, b, c) can be parsed like a function call (ignoring the !), so format it using the rules for function calls.

Special case macros

For macros which take a format string, if all other arguments are small, format the arguments before the format string on a single line if they fit, and format the arguments after the format string on a single line if they fit, with the format string on its own line. If the arguments are not small or do not fit, put each on its own line as with a function. For example:

println!(
    "Hello {} and {}",
    name1, name2,
);

assert_eq!(
    x, y,
    "x and y were not equal, see {}",
    reason,
);

Casts (as)

Put spaces before and after as:

let cstr = "Hi\0" as *const str as *const [u8] as *const std::os::raw::c_char;

Chains of fields and method calls

A chain is a sequence of field accesses, method calls, and/or uses of the try operator ?. E.g., a.b.c().d or foo?.bar().baz?.

Format the chain on one line if it is "small" and otherwise possible to do so. If formatting on multiple lines, put each field access or method call in the chain on its own line, with the line-break before the . and after any ?. Block-indent each subsequent line:

let foo = bar
    .baz?
    .qux();

If the length of the last line of the first element plus its indentation is less than or equal to the indentation of the second line, then combine the first and second lines if they fit. Apply this rule recursively.

x.baz?
    .qux()

x.y.z
    .qux()

let foo = x
    .baz?
    .qux();

foo(
    expr1,
    expr2,
).baz?
    .qux();

Multi-line elements

If any element in a chain is formatted across multiple lines, put that element and any later elements on their own lines.

a.b.c()?
    .foo(
        an_expr,
        another_expr,
    )
    .bar
    .baz

Note there is block indent due to the chain and the function call in the above example.

Prefer formatting the whole chain in multi-line style and each element on one line, rather than putting some elements on multiple lines and some on a single line, e.g.,

// Better
self.pre_comment
    .as_ref()
    .map_or(false, |comment| comment.starts_with("//"))

// Worse
self.pre_comment.as_ref().map_or(
    false,
    |comment| comment.starts_with("//"),
)

Control flow expressions

This section covers if, if let, loop, while, while let, and for expressions.

Put the keyword, any initial clauses, and the opening brace of the block all on a single line, if they fit. Apply the usual rules for block formatting to the block.

If there is an else component, then put the closing brace, else, any following clause, and the opening brace all on the same line, with a single space before and after the else keyword:

if ... {
    ...
} else {
    ...
}

if let ... {
    ...
} else if ... {
    ...
} else {
    ...
}

If the control line needs to be broken, prefer to break before the = in * let expressions and before in in a for expression; block-indent the following line. If the control line is broken for any reason, put the opening brace on its own line, not indented. Examples:

while let Some(foo)
    = a_long_expression
{
    ...
}

for foo
    in a_long_expression
{
    ...
}

if a_long_expression
    && another_long_expression
    || a_third_long_expression
{
    ...
}

Where the initial clause spans multiple lines and ends with one or more closing parentheses, square brackets, or braces, and there is nothing else on that line, and that line is not indented beyond the indent on the first line of the control flow expression, then put the opening brace of the block on the same line with a preceding space. For example:

if !self.config.file_lines().intersects(
    &self.codemap.lookup_line_range(
        stmt.span,
    ),
) {  // Opening brace on same line as initial clause.
    ...
}

Single line if else

Put an if else or if let else on a single line if it occurs in expression context (i.e., is not a standalone statement), it contains a single else clause, and is small:

let y = if x { 0 } else { 1 };

// Examples that must be multi-line.
let y = if something_very_long {
    not_small
} else {
    also_not_small
};

if x {
    0
} else {
    1
}

Match

Prefer not to line-break inside the discriminant expression. Always break after the opening brace and before the closing brace. Block-indent the match arms once:

match foo {
    // arms
}

let x = match foo.bar.baz() {
    // arms
};

Use a trailing comma for a match arm if and only if not using a block.

Never start a match arm pattern with |:

match foo {
    // Don't do this.
    | foo => bar,
    // Or this.
    | a_very_long_pattern
    | another_pattern
    | yet_another_pattern
    | a_fourth_pattern => {
        ...
    }
}

Prefer:

match foo {
    foo => bar,
    a_very_long_pattern
    | another_pattern
    | yet_another_pattern
    | a_fourth_pattern => {
        ...
    }
}

Avoid splitting the left-hand side (before the =>) of a match arm where possible. If the right-hand side of the match arm is kept on the same line, never use a block (unless the block is empty).

If the right-hand side consists of multiple statements, or has line comments, or the start of the line does not fit on the same line as the left-hand side, use a block. Do not flatten a right-hand side block containing a single macro call because its expanded form could contain a trailing semicolon.

Block-indent the body of a block arm.

Examples:

match foo {
    foo => bar,
    a_very_long_pattern | another_pattern if an_expression() => {
        no_room_for_this_expression()
    }
    foo => {
        // A comment.
        an_expression()
    }
    foo => {
        let a = statement();
        an_expression()
    }
    bar => {}
    // Trailing comma on last item.
    foo => bar,
    baz => qux!(),
    lorem => {
        ipsum!()
    }
}

If the body is a single expression with no line comments and not a control flow expression, start it on the same line as the left-hand side. If not, then it must be in a block. Example:

match foo {
    // A combinable expression.
    foo => a_function_call(another_call(
        argument1,
        argument2,
    )),
    // A non-combinable expression
    bar => {
        a_function_call(
            another_call(
                argument1,
                argument2,
            ),
            another_argument,
        )
    }
}

Line-breaking

If using a block form on the right-hand side of a match arm makes it possible to avoid breaking on the left-hand side, do that:

    // Assuming the following line does not fit in the max width
    a_very_long_pattern | another_pattern => ALongStructName {
        ...
    },
    // Prefer this
    a_very_long_pattern | another_pattern => {
        ALongStructName {
            ...
        }
    }
    // To splitting the pattern.

Never break after => without using the block form of the body.

If the left-hand side must be split and there is an if clause, break before the if and block indent. In this case, always use a block body and start the body on a new line:

    a_very_long_pattern | another_pattern
        if expr =>
    {
        ...
    }

If required to break the pattern, put each clause of the pattern on its own line with no additional indent, breaking before the |. If there is an if clause, use the above form:

    a_very_long_pattern
    | another_pattern
    | yet_another_pattern
    | a_forth_pattern => {
        ...
    }
    a_very_long_pattern
    | another_pattern
    | yet_another_pattern
    | a_forth_pattern
        if expr =>
    {
        ...
    }

If the pattern is multi-line, and the last line is less wide than the indent, do not put the if clause on a new line. E.g.,

    Token::Dimension {
         value,
         ref unit,
         ..
    } if num_context.is_ok(context.parsing_mode, value) => {
        ...
    }

If every clause in a pattern is small, but the whole pattern does not fit on one line, then format the pattern across multiple lines with as many clauses per line as possible. Again, break before a |:

    foo | bar | baz
    | qux => {
        ...
    }

We define a pattern clause to be small if it fits on a single line and matches "small" in the following grammar:

small:
    - small_no_tuple
    - unary tuple constructor: `(` small_no_tuple `,` `)`
    - `&` small

small_no_tuple:
    - single token
    - `&` small_no_tuple

E.g., &&Some(foo) matches, Foo(4, Bar) does not.

Combinable expressions

Where a function call has a single argument, and that argument is formatted across multiple-lines, format the outer call as if it were a single-line call, if the result fits. Apply the same combining behaviour to any similar expressions which have multi-line, block-indented lists of sub-expressions delimited by parentheses (e.g., macros or tuple struct literals). E.g.,

foo(bar(
    an_expr,
    another_expr,
))

let x = foo(Bar {
    field: whatever,
});

foo(|param| {
    action();
    foo(param)
})

let x = combinable([
    an_expr,
    another_expr,
]);

let arr = [combinable(
    an_expr,
    another_expr,
)];

Apply this behavior recursively.

For a function with multiple arguments, if the last argument is a multi-line closure with an explicit block, there are no other closure arguments, and all the arguments and the first line of the closure fit on the first line, use the same combining behavior:

foo(first_arg, x, |param| {
    action();
    foo(param)
})

Ranges

Do not put spaces in ranges, e.g., 0..10, x..=y, ..x.len(), foo...

When writing a range with both upper and lower bounds, if the line must be broken within the range, break before the range operator and block indent the second line:

a_long_expression
    ..another_long_expression

For the sake of indicating precedence, if either bound is a compound expression, use parentheses around it, e.g., ..(x + 1), (x.f)..(x.f.len()), or 0..(x - 10).

Hexadecimal literals

Hexadecimal literals may use upper- or lower-case letters, but they must not be mixed within the same literal. Projects should use the same case for all literals, but we do not make a recommendation for either lower- or upper-case.

Patterns

Format patterns like their corresponding expressions. See the section on match for additional formatting for patterns in match arms.

类型和约束

单行格式

  • [T] 不带空格
  • [T; expr],例如:[u32; 42][Vec<Foo>; 10 * 2 + foo()](冒号后空格,方括号无空格)
  • *const T*mut T*后无空格,类型前有空格)
  • &'a T&T&'a mut T&mut T& 后无空格,其他单词用单个空格隔开)
  • unsafe extern "C" fn<'a, 'b, 'c>(T, U, V) -> Wfn()(关键字和符号后有空格,逗号后有空格,逗号后无空格,括号后无空格)。
  • ! 与其他类型名称一样,Name
  • (A, B, C, D)(逗号后有空格,双引号无空格,除非是单元组,否则逗号后无空格)
  • <Baz<T> as SomeTrait>::Foo::Bar 或 Foo::Bar::Foo::Bar:: 或尖括号后无空格,as 前后各有一个空格)
  • Foo::Bar<T, U, V>(逗号后有空格,逗号前无空格,尖括号前后无空格)
  • T + T + T(类型和 之间用一个空格)。
  • impl T + T + T(关键字、类型和 `+`` 之间用空格隔开)。

类型中使用的括号不要空格,例如 (Foo)

换行

尽可能避免在类型中换行。最好在最外层的范围内换行,例如,最好使用以下形式:

Foo<
    Bar,
    Baz<Type1, Type2>,
>

而不采用:

Foo<Bar, Baz<
    Type1,
    Type2,
>>

如果一个类型需要换行才能适应,本节概括了在必要时应在何处换行。

必要时,在 ; 后分隔 [T; expr]

按照函数声明的规则断开函数类型。

按照泛型的规则断开泛型类型。

断开带 + 的类型,方法是在 + 之前断开,并在随后的行中用块缩进。断开此类类型时,应在每个 + 之前断开:

impl Clone
    + Copy
    + Debug

Box<
    Clone
    + Copy
    + Debug
>

其他风格建议

表达式

尽可能使用 Rust 面向表达式的特性;

// 使用
let x = if y { 1 } else { 0 };
// 不使用
let x;
if y {
    x = 1;
} else {
    x = 0;
}

命名规范

  • 类型应为首字母大写的驼峰命名法(UpperCamelCase),
  • 枚举变量应为首字母大写的驼峰命名法(UpperCamelCase),
  • 结构体字段应使用纯小写下划线命名法(snake_case),
  • 函数和方法名称应使用纯小写下划线命名法(snake_case),
  • 局部变量应为纯小写下划线命名法(snake_case),
  • 宏名称应为纯小写下划线命名法(snake_case),
  • 常量(常量和不可变静态)应使用纯大写下划线命名(SCREAMING_SNAKE_CASE
  • 当名称是保留字(如 crate)而禁止使用时,要么使用原始标识符(r#crate),要么使用尾部下划线(crate_)——不要拼错单词 (krate)。

模块

尽可能避免使用 #[path] 标注。

Cargo.toml 的约定

格式约定

使用与 Rust 代码相同的行宽和缩进。

在一个表块的最后一个键值对与下一表块的标题之间空一行。在表块标题和该表块中的键值对之间,或同一表块中的键值对之间,不要加空行。

[package] 表块外,按字母顺序排列各部分中的键名。将 [package] 表块放在文件的顶部;将 nameversion 键按顺序放在该表块的顶部,接着除 description 外的其余键按字母顺序排列,最后是该表块的末尾的 description

任何标准键名都不要使用引号,使用裸键。只有名称需要引号的非标准键才使用引号,并尽可能避免引入此类键名。详情请参见 TOML 规范

在键和值之间的 = 前后各留一个空格。不要缩进任何键名;所有键名都从一行的开头开始。

对于包含多行的字符串值,如 crate 说明,应使用多行字符串(而不是换行符)。

对于数组值(如特性列表),如果合适,可将整个列表与键放在同一行。否则,使用分块缩进:在开头的方括号后加一个换行符,每个项缩进一级,每个项(包括最后一个项)后加一个逗号,最后一个项后将结尾的方括号放在一行的开头。

对于数组值(如特征列表),如果合适,可将整个列表与键放在同一行。否则,使用分块缩进:在开头的方括号后加一个换行符,每个项目缩进一级,每个项目(包括最后一个项目)后加一个逗号,最后一个项目后将结尾的方括号放在一行的开头。

some_feature = [
    "another_feature",
    "yet_another_feature",
    "some_dependency?/some_feature",
]

对于表值,例如带有路径的 crate 依赖关系,如果合适的话,使用大括号和逗号将整个表写在与键相同的行上。如果整个表格不能与关键字写在同一行,则应使用键值对将其分隔成一个单独的部分:

[dependencies]
crate1 = { path = "crate1", version = "1.2.3" }

[dependencies.extremely_long_crate_name_goes_here]
path = "extremely_long_path_name_goes_right_here"
version = "4.5.6"

元数据约定

作者列表(若有的话)应由字符串组成,每个字符串包含一个作者姓名,后面是置于尖括号内的电子邮件地址: Full Name <email@address>。不应包含空电子邮件地址或没有电子邮件地址的姓名。(作者列表中也可以包含没有相关姓名的邮件列表地址)。

许可证字段必须包含有效的 SPDX 表达式,并使用有效的 SPDX 许可证名称。(作为例外,根据普遍惯例,许可证字段可以使用 / 代替 OR,例如 MIT/Apache-2.0)。

主页字段(若有的话)必须包含一个单独的 URL,包括协议(如 https://example.org/,而不只是 example.org)。

在描述字段中,按 80 列对文本进行换行。不要以 crate 的名称作为描述字段的开头(例如 "cratename is a ...");只需描述 crate 本身。如果提供的是多句描述,第一句应单独成行并概括 crate,就像电子邮件或提交信息的主题一样;随后的句子可以更详细地描述 crate。

指导原则和基本原理

在确定风格指南时,风格团队遵循以下指导原则(按大致优先顺序排列):

  • 可读性
    • 可扫描性
    • 避免格式误导
    • 可访问性——使用各种硬件(包括非视觉可访问界面)的用户均可阅读辑
    • 代码在没有语法高亮或集成开发环境辅助的情况下的可读性,如 rustc 错误信息、差异内容、grep 和其他纯文本情况下的可读性
  • 美感
    • 美感
    • 与其他语言/工具保持一
  • 特殊性
    • 与版本控制实践兼容——保留差异、便于合并等
    • 防止向右偏移(preventing rightward drift)
    • 尽量减少垂直空
  • 应用
    • 易于手动应用
    • 易于实施(在 `rustfmt`` 和其他工具/编辑器/代码生成器中)
    • 内部一致性
    • 格式规则简单

本章记录了开发版特定(nightly-only,直译为:夜间特定)语法的样式和格式。风格指南的其余部分记录了 Rust 稳定语法的风格;开发版特定语法仅出现在本章。这里的每一节都包含了特性门类的名称,因此在 Rust 代码库中搜索(例如 git grep)开发版特性时,也会出现样式指南部分。

开发版特定语法的样式和格式应从本章移除,并在稳定化时整合到样式指南的相应章节中。

与样式指南的其他部分相比,本章的稳定性无法保证。有关对本章进行中断更改的夜间格式化程序,请参阅样式团队政策。