表达式

代码块

块表达式在初始 { 之后和结尾 } 之前必须有一个换行符,除非它符合基于另一条样式规则编写为单行的条件。

块之前的关键字(例如 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
    }
}
}

如果一个块有属性,请将其放在块之前的单独一行上

#![allow(unused)]
fn main() {
fn block_as_stmt() {
    #[an_attribute]
    {
        #![an_inner_attribute]

        // a comment in a block
        the_value
    }
}
}

避免在与大括号相同的行上编写注释。

将空块写为 {}

在以下情况下,将块写在单行上

  • 它要么用在表达式位置(而不是语句位置),要么是用在语句位置的 unsafe 块,
  • 它包含单行表达式且不包含语句,并且
  • 它不包含注释

对于单行块,在开括号后和闭括号前放置空格。

示例

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

闭包

不要在第一个 | 之前放置任何额外的空格(除非闭包以关键字(如 move)为前缀);在第二个 | 和闭包的表达式之间放置一个空格。在 | 之间,使用函数定义语法,但在可能的情况下省略类型。

如果可能,请使用不带封闭 {} 的闭包。当您有返回类型、有语句、闭包内有注释或主体表达式是跨越多行的控制流表达式时,添加 {}。如果使用大括号,请遵循上面关于块的规则。示例

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

结构体字面量

如果结构体字面量很小,则将其格式化为单行,并且不要使用尾随逗号。否则,将其拆分为多行,每个字段在其自己的块缩进的行上,并使用尾随逗号。

对于每个 field: value 条目,仅在冒号后放置一个空格。

在开括号前放置一个空格。在单行形式中,在开括号后和闭括号前放置空格。

#![allow(unused)]
fn main() {
Foo { field1, field2: 0 }
let f = Foo {
    field1,
    field2: an_expr,
};
}

函数式记录更新语法被视为一个字段,但它绝不能有尾随逗号。不要在 .. 之后放置空格。

#![allow(unused)]
fn main() {
let f = Foo {
    field1,
    ..an_expr
};
}

元组字面量

尽可能使用单行形式。不要在开括号和第一个元素之间,或最后一个元素和闭括号之间放置空格。用逗号后跟一个空格分隔元素。

如果单行形式不可行,请将元组写在多行上,元组的每个元素都在其自己的块缩进的行上,并使用尾随逗号。

#![allow(unused)]
fn main() {
(a, b, c)

let x = (
    a_long_expr,
    another_very_long_expr,
);
}

元组结构体字面量

标识符和开括号之间不要放置空格。否则,请遵循元组字面量的规则

#![allow(unused)]
fn main() {
Foo(a, b, c)

let x = Foo(
    a_long_expr,
    another_very_long_expr,
);
}

枚举字面量

遵循各种结构体字面量的格式化规则。除非枚举在 prelude 中,否则首选使用枚举的名称作为限定名称

#![allow(unused)]
fn main() {
Foo::Bar(a, b)
Foo::Baz {
    field1,
    field2: 1001,
}
Ok(an_expr)
}

数组字面量

将小数组字面量写在单行上。不要在开方括号和第一个元素之间,或最后一个元素和闭方括号之间放置空格。用逗号后跟一个空格分隔元素。

如果使用重复初始化器,则仅在分号后放置一个空格。

如果使用 vec! 或类似的类数组宏,则应用相同的规则;始终将方括号与此类宏一起使用。示例

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

对于必须跨行断开的数组,如果使用重复初始化器,则在 ; 之后而不是之前断开。否则,请遵循下面关于函数调用的规则。在任何情况下,块缩进初始化器的内容,并在开方括号后和闭方括号前放置换行符

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

数组访问、索引和切片

不要在方括号周围放置空格。尽可能避免断行。永远不要在目标表达式和开方括号之间断行。如果索引表达式必须断开到后续行,或者本身跨越多行,则块缩进索引表达式,并在开方括号后和闭方括号前放置换行符

示例

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

一元运算

不要在一元运算符及其操作数之间包含空格(即 !x,而不是 ! x)。但是,&mut 之后必须有一个空格。避免在一元运算符及其操作数之间断行。

二元运算

包括二元运算符周围的空格(即 x + 1,而不是 x+1)(包括 = 和其他赋值运算符,如 +=*=)。

对于比较运算符,因为对于 T op U,也实现了 &T op &U:如果您有 t: &Tu: U,则首选 *t op u 而不是 t op &u。一般来说,在表达式中,除非必要(例如,为了避免不必要的昂贵操作),否则首选解引用而不是取引用。

自由使用括号;不要因为优先级而一定省略它们。工具不应自动插入或删除括号。不要使用空格来表示优先级。

如果断行,则块缩进每个后续行。对于赋值运算符,在运算符之后断开;对于所有其他运算符,将运算符放在后续行上。将每个子表达式放在自己的行上

#![allow(unused)]
fn main() {
foo_bar
    + bar
    + baz
    + qux
    + whatever
}

首选在赋值运算符(=+= 等)处断行,而不是在其他二元运算符处断行。

类型转换 (as)

像二元运算符一样格式化 as 类型转换。特别是,始终包含 as 周围的空格,如果断行,则在 as 之前断开(永远不在之后),并块缩进后续行。使用类型的规则格式化右侧的类型。

但是,与其他二元运算符不同,如果链接一系列需要断行的 as 类型转换,并且在第一个 as 之前断行足以使其余部分适合下一行,则不要在任何后续 as 之前断开;相反,将一系列类型都保留在同一行上

#![allow(unused)]
fn main() {
let cstr = very_long_expression()
    as *const str as *const [u8] as *const std::os::raw::c_char;
}

如果后续行仍然需要断行,则像其他二元运算符一样,在每个 as 之前断开并块缩进。

控制流

不要为 ifwhile 表达式包含多余的括号。

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

比以下更好

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

如果多余的括号使算术或逻辑表达式更容易理解,则包含多余的括号((x * 15) + (y * 20) 很好)

函数调用

不要在函数名称和开括号之间放置空格。

不要在参数和其后的逗号之间放置空格。

在参数和其前面的逗号之间放置空格。

首选不在被调用者表达式中换行。

单行调用

不要在函数名称和开括号之间、开括号和第一个参数之间,或最后一个参数和闭括号之间放置空格。

不要在最后一个参数后放置逗号。

#![allow(unused)]
fn main() {
foo(x, y, z)
}

多行调用

如果函数调用不大,否则会超出最大宽度,或者任何参数或被调用者是多行的,则跨多行格式化调用。在这种情况下,将每个参数放在其自己的块缩进的行上,在开括号后和闭括号前断开,并使用尾随逗号

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

方法调用

遵循函数调用的规则。

不要在 . 周围放置任何空格。

#![allow(unused)]
fn main() {
x.foo().bar().baz(x, y, z);
}

宏使用

如果宏可以像其他构造一样解析,则像那些构造一样格式化它。例如,宏使用 foo!(a, b, c) 可以像函数调用一样解析(忽略 !),因此使用函数调用的规则格式化它。

风格指南为语言或标准库中的特定宏定义了特定格式。风格指南没有为任何第三方宏定义格式,即使与语言或标准库中的宏类似。

格式字符串宏

对于采用格式字符串的宏,如果所有其他参数都很小,则如果它们适合,则将格式字符串之前的参数格式化为单行,如果它们适合,则将格式字符串之后的参数格式化为单行,并将格式字符串放在其自己的行上。如果参数不大或不适合,则像函数一样将每个参数放在其自己的行上。例如

#![allow(unused)]
fn main() {
println!(
    "Hello {} and {}",
    name1, name2,
);

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

字段和方法调用链

链是一系列字段访问、方法调用和/或 try 运算符 ? 的使用。例如,a.b.c().dfoo?.bar().baz?

如果链“很小”并且在其他方面可能,则在一行上格式化链。如果跨多行格式化,则将链中的每个字段访问或方法调用放在其自己的行上,断行在 . 之前和任何 ? 之后。块缩进每个后续行

#![allow(unused)]
fn main() {
let foo = bar
    .baz?
    .qux();
}

如果第一个元素的最后一行的长度加上其缩进小于或等于第二行的缩进,则如果它们适合,则合并第一行和第二行。递归地应用此规则。

#![allow(unused)]
fn main() {
x.baz?
    .qux()

x.y.z
    .qux()

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

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

多行元素

如果链中的任何元素跨多行格式化,则将该元素和任何后续元素放在它们自己的行上。

#![allow(unused)]
fn main() {
a.b.c()?
    .foo(
        an_expr,
        another_expr,
    )
    .bar
    .baz
}

请注意,由于链和上面示例中的函数调用,存在块缩进。

首选以多行样式格式化整个链并将每个元素放在一行上,而不是将某些元素放在多行上,而将某些元素放在单行上,例如,

#![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("//"),
)
}

控制流表达式

本节涵盖 ifif letloopwhilewhile letfor 表达式。

如果关键字、任何初始子句和块的开括号都适合,则将它们全部放在一行上。将 块格式化 的常用规则应用于块。

如果有 else 组件,则将闭括号、else、任何后续子句和开括号都放在同一行上,else 关键字前后各一个空格

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

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

如果控制行需要断开,则首选在 * let 表达式中的 = 之前和 for 表达式中的 in 之前断开;块缩进以下行。如果控制行因任何原因而断开,则将开括号放在其自己的行上,而不是缩进。示例

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

如果初始子句跨越多行并以一个或多个闭括号、方括号或大括号结尾,并且该行上没有其他内容,并且该行的缩进不超过控制流表达式的第一行的缩进,则将块的开括号与前导空格放在同一行上。例如

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

单行 if else

如果 if elseif let else 出现在表达式上下文中(即,不是独立语句),它包含单个 else 子句,并且很小,则将其放在单行上

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

首选不在判别式表达式内换行。始终在开括号后和闭括号前断开。块缩进 match 分支一次

#![allow(unused)]
fn main() {
match foo {
    // arms
}

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

当且仅当不使用块时,才对 match 分支使用尾随逗号。

永远不要以 | 开头 match 分支模式

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

首选

#![allow(unused)]
fn main() {
match foo {
    foo => bar,
    a_very_long_pattern
    | another_pattern
    | yet_another_pattern
    | a_fourth_pattern => {
        ...
    }
}
}

尽可能避免拆分 match 分支的左侧(=> 之前)。如果 match 分支的右侧保持在同一行上,则永远不要使用块(除非块为空)。

如果右侧由多个语句组成,或者有行注释,或者该行的开头与左侧不在同一行上,则使用块。不要展平包含单个宏调用的右侧块,因为其展开形式可能包含尾随分号。

块缩进块分支的主体。

示例

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

如果主体是单个表达式,没有行注释,并且不是控制流表达式,则将其与左侧放在同一行上开始。否则,它必须在块中。示例

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

断行

如果在 match 分支的右侧使用块形式可以避免在左侧断开,请这样做

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

永远不要在 => 之后断开,而不使用主体的块形式。

如果左侧必须拆分并且有 if 子句,则在 if 之前断开并块缩进。在这种情况下,始终使用块主体并在新行上启动主体

#![allow(unused)]
fn main() {
    a_very_long_pattern | another_pattern
        if expr =>
    {
        ...
    }
}

如果需要断开模式,请将模式的每个子句放在其自己的行上,且不添加额外的缩进,在 | 之前断开。如果有 if 子句,请使用上述形式

#![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 子句放在新行上。例如,

#![allow(unused)]
fn main() {
    Token::Dimension {
         value,
         ref unit,
         ..
    } if num_context.is_ok(context.parsing_mode, value) => {
        ...
    }
}

如果模式中的每个子句都很小,但整个模式不适合在一行上,则跨多行格式化模式,每行尽可能多地包含子句。再次,在 | 之前断开

#![allow(unused)]
fn main() {
    foo | bar | baz
    | qux => {
        ...
    }
}

我们将模式子句定义为,如果它适合单行并且在以下语法中匹配“小”

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

small_no_tuple:
    - single token
    - `&` small_no_tuple

例如,&&Some(foo) 匹配,Foo(4, Bar) 不匹配。

可组合表达式

如果函数调用具有单个参数,并且该参数跨多行格式化,则如果结果适合,则将外部调用格式化为单行调用。将相同的组合行为应用于任何类似的表达式,这些表达式具有由括号分隔的多行、块缩进的子表达式列表(例如,宏或元组结构体字面量)。例如,

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

递归地应用此行为。

对于具有多个参数的函数,如果最后一个参数是具有显式块的多行闭包,则没有其他闭包参数,并且所有参数和闭包的第一行都适合第一行,则使用相同的组合行为

#![allow(unused)]
fn main() {
foo(first_arg, x, |param| {
    action();
    foo(param)
})
}

范围

不要在范围中放置空格,例如,0..10x..=y..x.len()foo..

当编写具有上限和下限的范围时,如果必须在范围内断行,则在范围运算符之前断开,并块缩进第二行

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

为了指示优先级,如果任一边界是复合表达式,请在其周围使用括号,例如,..(x + 1)(x.f)..(x.f.len())0..(x - 10)

十六进制字面量

十六进制字面量可以使用大写或小写字母,但它们不得在同一字面量中混合使用。项目应为所有字面量使用相同的大小写,但我们不建议使用小写或大写。

模式

像格式化其对应的表达式一样格式化模式。有关 match 分支中模式的其他格式,请参阅关于 match 的章节。