Rust 语言风格指南

动机——为什么要使用格式化工具?

格式化代码是一项耗时耗力的机械性工作。如果使用自动格式化工具,开发者就可以从这项工作中解脱出来,专注于更重要的事情。

此外,通过坚持使用既定的风格指南(如本指南),开发者无需制定特别的风格规则,也无需与其他开发者争论应使用何种样式规则,从而节省了时间、沟通成本和精神耗损。

人类通过模式匹配来理解信息。通过确保所有 Rust 代码具有相似的格式,就能减少理解新项目所需的脑力劳动,从而降低新开发人员的入门门槛。

因此,使用格式化工具(如 rustfmt)可以提高工作效率,而使用社区一致的格式规约(通常是使用格式化工具的默认设置)则会带来更大的好处。

默认 Rust 风格

《Rust 语言风格指南》定义了默认 Rust 风格,并建议开发者和工具遵循默认 Rust 样式。rustfmt 等工具使用此风格指南作为默认风格的参考。本风格指南中的所有内容,无论是否使用“必须”等语言或“插入空格......”或“在......后换行”等命令式语气,都是指默认样式。

这不应被解释为禁止开发人员遵循非默认样式,或禁止工具添加任何特定的配置选项。

格式约定

缩进和行宽

  • 使用空格,而不是制表符。
  • 每级缩进必须是 4 个空格(也就是说,字符串字面量和注释之外的所有缩进空格数都必须是 4 的倍数)。
  • 一行的最大宽度为 100 个字符。

块缩进

与视觉化缩进(visual indent)相比,更倾向于分块缩进:

#![allow(unused)]
fn main() {
// 块缩进
a_function_call(
    foo,
    bar,
);

// 视觉化缩进
a_function_call(foo,
                bar);
}

这样做的差异就会变小(例如,在上例中重命名了a_function_call),向右移动的情况也会减少。

Trailing commas

In comma-separated lists of any kind, use a trailing comma when followed by a newline:

#![allow(unused)]
fn main() {
function_call(
    argument,
    another_argument,
);

let array = [
    element,
    another_element,
    yet_another_element,
];
}

This makes moving code (e.g., by copy and paste) easier, and makes diffs smaller, as appending or removing items does not require modifying another line to add or remove a comma.

Blank lines

Separate items and statements by either zero or one blank lines (i.e., one or two newlines). E.g,

#![allow(unused)]
fn main() {
fn foo() {
    let x = ...;

    let y = ...;
    let z = ...;
}

fn bar() {}
fn baz() {}
}

Module-level items

Statements

Expressions

Types

Comments

The following guidelines for comments are recommendations only, a mechanical formatter might skip formatting of comments.

Prefer line comments (//) to block comments (/* ... */).

When using line comments, put a single space after the opening sigil.

When using single-line block comments, put a single space after the opening sigil and before the closing sigil. For multi-line block comments, put a newline after the opening sigil, and a newline before the closing sigil.

Prefer to put a comment on its own line. Where a comment follows code, put a single space before it. Where a block comment appears inline, use surrounding whitespace as if it were an identifier or keyword. Do not include trailing whitespace after a comment or at the end of any line in a multi-line comment. Examples:

#![allow(unused)]
fn main() {
// A comment on an item.
struct Foo { ... }

fn foo() {} // A comment after an item.

pub fn foo(/* a comment before an argument */ x: T) {...}
}

Comments should usually be complete sentences. Start with a capital letter, end with a period (.). An inline block comment may be treated as a note without punctuation.

Source lines which are entirely a comment should be limited to 80 characters in length (including comment sigils, but excluding indentation) or the maximum width of the line (including comment sigils and indentation), whichever is smaller:

#![allow(unused)]
fn main() {
// This comment goes up to the ................................. 80 char margin.

{
    // This comment is .............................................. 80 chars wide.
}

{
    {
        {
            {
                {
                    {
                        // This comment is limited by the ......................... 100 char margin.
                    }
                }
            }
        }
    }
}
}

Doc comments

Prefer line comments (///) to block comments (/** ... */).

Prefer outer doc comments (/// or /** ... */), only use inner doc comments (//! and /*! ... */) to write module-level or crate-level documentation.

Put doc comments before attributes.

Attributes

Put each attribute on its own line, indented to the level of the item. In the case of inner attributes (#!), indent it to the level of the inside of the item. Prefer outer attributes, where possible.

For attributes with argument lists, format like functions.

#![allow(unused)]
fn main() {
#[repr(C)]
#[foo(foo, bar)]
#[long_multi_line_attribute(
    split,
    across,
    lines,
)]
struct CRepr {
    #![repr(C)]
    x: f32,
    y: f32,
}
}

For attributes with an equal sign, put a single space before and after the =, e.g., #[foo = 42].

There must only be a single derive attribute. Note for tool authors: if combining multiple derive attributes into a single attribute, the ordering of the derived names must generally be preserved for correctness: #[derive(Foo)] #[derive(Bar)] struct Baz; must be formatted to #[derive(Foo, Bar)] struct Baz;.

small items

In many places in this guide we specify formatting that depends on a code construct being small. For example, single-line vs multi-line struct literals:

#![allow(unused)]
fn main() {
// Normal formatting
Foo {
    f1: an_expression,
    f2: another_expression(),
}

// "small" formatting
Foo { f1, f2 }
}

We leave it to individual tools to decide on exactly what small means. In particular, tools are free to use different definitions in different circumstances.

Some suitable heuristics are the size of the item (in characters) or the complexity of an item (for example, that all components must be simple names, not more complex sub-expressions). For more discussion on suitable heuristics, see this issue.

Non-formatting conventions

Cargo.toml conventions

Principles used for deciding these guidelines

程序项

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

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

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

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

Function definitions

In Rust, people often find functions by searching for fn [function-name], so the formatting of function definitions must enable this.

The proper ordering and spacing is:

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

Avoid comments within the signature itself.

If the function signature does not fit on one line, then break after the opening parenthesis and before the closing parenthesis and put each argument on its own block-indented line. For example,

#![allow(unused)]
fn main() {
fn foo(
    arg1: i32,
    arg2: i32,
) -> i32 {
    ...
}
}

Note the trailing comma on the last argument.

Tuples and tuple structs

Write the type list as you would a parameter list to a function.

Build a tuple or tuple struct as you would call a function.

Single-line

#![allow(unused)]
fn main() {
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):

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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.,

#![allow(unused)]
fn main() {
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:

#![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>
{
    ...
}
}

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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
impl Bar
    for Foo
{
    ...
}
}

Extern crate

extern crate foo;

Use spaces around keywords, no spaces around the semicolon.

Modules

#![allow(unused)]
fn main() {
mod foo {
}
}
#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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 =:

#![allow(unused)]
fn main() {
<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:

#![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>;
}

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

#![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>,
}

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:

#![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 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.,

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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.

#![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
};
}

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:

#![allow(unused)]
fn main() {
use d;
use c;

use b;
use a;
}

output:

#![allow(unused)]
fn main() {
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,

#![allow(unused)]
fn main() {
use a::b::{
    x, y, z,
    u::{...},
    w::{...},
};
}

Merging/un-merging imports

An example:

#![allow(unused)]
fn main() {
// 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 语句

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

#![allow(unused)]
fn main() {
// 一条注释。
let pattern: Type = expr;

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

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

#![allow(unused)]
fn main() {
let pattern: Type =
    expr;
}

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

#![allow(unused)]
fn main() {
let pattern:
    Type =
    expr;
}

例如:

#![allow(unused)]
fn main() {
let Foo {
    f: abcd,
    g: qwer,
}: Foo<Bar> =
    Foo { f, g };

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

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

示例:

#![allow(unused)]
fn main() {
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 blocks (let-else statements)

A let statement can contain an else component, making it a let-else statement. In this case, always apply the same formatting rules to the components preceding the else block (i.e. the let pattern: Type = initializer_expr portion) as described for other let statements.

Format the entire let-else statement on a single line if all the following are true:

  • the entire statement is short
  • the else block contains only a single-line expression and no statements
  • the else block contains no comments
  • the let statement components preceding the else block can be formatted on a single line
#![allow(unused)]
fn main() {
let Some(1) = opt else { return };
}

Otherwise, the let-else statement requires some line breaks.

If breaking a let-else statement across multiple lines, never break between the else and the {, and always break before the }.

If the let statement components preceding the else can be formatted on a single line, but the let-else does not qualify to be placed entirely on a single line, put the else { on the same line as the initializer expression, with a space between them, then break the line after the {. Indent the closing } to match the let, and indent the contained block one step further.

#![allow(unused)]
fn main() {
let Some(1) = opt else {
    return;
};

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

If the let statement components preceding the else can be formatted on a single line, but the else { does not fit on the same line, break the line before the else.

#![allow(unused)]
fn main() {
    let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name
    else {
        return;
    };
}

If the initializer expression is multi-line, put the else keyword and opening brace of the block (i.e. else {) on the same line as the end of the initializer expression, with a space between them, if and only if all the following are true:

  • The initializer expression ends with one or more closing parentheses, square brackets, and/or braces
  • There is nothing else on that line
  • That line has the same indentation level as the initial let keyword.

For example:

#![allow(unused)]
fn main() {
let Some(x) = y.foo(
    "abc",
    fairly_long_identifier,
    "def",
    "123456",
    "string",
    "cheese",
) else {
    bar()
}
}

Otherwise, put the else keyword and opening brace on the next line after the end of the initializer expression, with the else keyword at the same indentation level as the let keyword.

For example:

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

Macros in statement position

For a macro use in statement position, use parentheses or square brackets as delimiters, and terminate it with a semicolon. Do not put spaces around the name, !, the delimiters, or the ;.

#![allow(unused)]
fn main() {
// A comment.
a_macro!(...);
}

Expressions in statement position

Do not put space between the expression and the semicolon.

<expr>;

Terminate all expressions in statement position with a semicolon, unless they end with a block or are used as the value for a block.

E.g.,

#![allow(unused)]
fn main() {
{
    an_expression();
    expr_as_value()
}

return foo();

loop {
    break;
}
}

Use a semicolon where an expression has void type, even if it could be propagated. E.g.,

#![allow(unused)]
fn main() {
fn foo() { ... }

fn bar() {
    foo();
}
}

表达式

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

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

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
|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.

#![allow(unused)]
fn main() {
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 ...

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
(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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
if true {
}
}

is better than

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
a_function_call(
    arg1,
    a_nested_call(a, b),
)
}

Method calls

Follow the function rules for calling.

Do not put any spaces around the ..

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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.

#![allow(unused)]
fn main() {
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.,

#![allow(unused)]
fn main() {
// 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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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 |:

#![allow(unused)]
fn main() {
match foo {
    // Don't do this.
    | foo => bar,
    // Or this.
    | a_very_long_pattern
    | another_pattern
    | yet_another_pattern
    | a_fourth_pattern => {
        ...
    }
}
}

Prefer:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
    // 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:

#![allow(unused)]
fn main() {
    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:

#![allow(unused)]
fn main() {
    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.,

#![allow(unused)]
fn main() {
    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 |:

#![allow(unused)]
fn main() {
    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.,

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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:

#![allow(unused)]
fn main() {
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)

换行

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

#![allow(unused)]
fn main() {
Foo<
    Bar,
    Baz<Type1, Type2>,
>
}

而不采用:

#![allow(unused)]
fn main() {
Foo<Bar, Baz<
    Type1,
    Type2,
>>
}

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

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

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

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

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

#![allow(unused)]
fn main() {
impl Clone
    + Copy
    + Debug

Box<
    Clone
    + Copy
    + Debug
>
}

其他风格建议

表达式

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

#![allow(unused)]
fn main() {
// 使用
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 说明,应使用多行字符串(而不是换行符)。

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

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

#![allow(unused)]
fn main() {
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)开发版特性时,也会出现样式指南部分。

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

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