02-表达式

表达式 — The Rust Reference

译文 · 基于 The Rust Reference

原文链接: https://doc.rust-lang.org/reference/expressions.html

r[expr]

表达式

r[expr.syntax]

Expression ->
      ExpressionWithoutBlock
    | ExpressionWithBlock

ExpressionWithoutBlock ->
    OuterAttribute* ExpressionWithoutBlockNoAttrs

ExpressionWithoutBlockNoAttrs ->
      LiteralExpression
    | PathExpression
    | OperatorExpression
    | GroupedExpression
    | ArrayExpression
    | AwaitExpression
    | IndexExpression
    | TupleExpression
    | TupleIndexingExpression
    | StructExpression
    | CallExpression
    | MethodCallExpression
    | FieldExpression
    | ClosureExpression
    | AsyncBlockExpression
    | ContinueExpression
    | BreakExpression
    | RangeExpression
    | ReturnExpression
    | UnderscoreExpression
    | MacroInvocation

ExpressionWithBlock ->
    OuterAttribute* ExpressionWithBlockNoAttrs

ExpressionWithBlockNoAttrs ->
      BlockExpression
    | ConstBlockExpression
    | UnsafeBlockExpression
    | LoopExpression
    | IfExpression
    | MatchExpression

r[expr.intro] 表达式可以有两种角色:它总是产生一个值,并且可能具有效果(也称为“副作用”)。

r[expr.evaluation] 表达式求值为一个值,并在求值期间产生效果。

r[expr.operands] 许多表达式包含子表达式,称为该表达式的操作数。

r[expr.behavior] 每种表达式的含义规定了若干事项:

  • 求值该表达式时是否求值其操作数
  • 求值操作数的顺序
  • 如何组合操作数的值以得到该表达式的值

r[expr.structure] 如此,表达式的结构决定了执行的结构。块只是另一种表达式,因此块、语句、表达式以及块可以彼此递归嵌套到任意深度。

注意 我们为表达式的操作数命名以便讨论,但这些名称并不稳定,可能会更改。

r[expr.precedence]

表达式优先级

Rust 运算符和表达式的优先级按如下顺序从强到弱排列。同一优先级的二元运算符按其结合性所给出的顺序分组。

运算符/表达式结合性
[路径][expr.path]
[方法调用][expr.method]
[字段表达式][expr.field]从左到右
[函数调用][expr.call],[数组索引][expr.array.index]
[?][expr.try]
一元 [-][expr.negate] [!][expr.negate] [*][expr.deref] [借用][expr.operator.borrow]
[as][expr.as]从左到右
[*][expr.arith-logic] [/][expr.arith-logic] [%][expr.arith-logic]从左到右
[+][expr.arith-logic] [-][expr.arith-logic]从左到右
[<<][expr.arith-logic] [>>][expr.arith-logic]从左到右
[&][expr.arith-logic]从左到右
[^][expr.arith-logic]从左到右
[|][expr.arith-logic]从左到右
[==][expr.cmp] [!=][expr.cmp] [<][expr.cmp] [>][expr.cmp] [<=][expr.cmp] [>=][expr.cmp]需要括号
[&&][expr.bool-logic]从左到右
[||][expr.bool-logic]从左到右
[..][expr.range] [..=][expr.range]需要括号
[=][expr.assign] [+=][expr.compound-assign] [-=][expr.compound-assign] [*=][expr.compound-assign] [/=][expr.compound-assign] [%=][expr.compound-assign]
[&=][expr.compound-assign] [|=][expr.compound-assign] [^=][expr.compound-assign] [<<=][expr.compound-assign] [>>=][expr.compound-assign]
从右到左
[return][expr.return] [break][expr.loop.break] [闭包][expr.closure]

r[expr.operand-order]

操作数的求值顺序

r[expr.operand-order.default] 下列表达式都以相同方式求值其操作数,如列表之后所述。其他表达式要么没有操作数,要么按各自页面所述有条件地求值操作数。

  • 解引用表达式
  • 错误传播表达式
  • 取反表达式
  • 算术与逻辑二元运算符
  • 比较运算符
  • 类型转换表达式
  • 分组表达式
  • 数组表达式
  • await 表达式
  • 索引表达式
  • 元组表达式
  • 元组索引表达式
  • 结构体表达式
  • 调用表达式
  • 方法调用表达式
  • 字段表达式
  • break 表达式
  • 区间表达式
  • return 表达式

r[expr.operand-order.operands-before-primary] 这些表达式的操作数在应用该表达式的效果之前求值。接受多个操作数的表达式按源代码中书写的顺序从左到右求值。

注意 哪些子表达式是某表达式的操作数,由上一节的表达式优先级决定。

例如,两次 next 方法调用总是按相同顺序进行:

1
2
3
4
5
6
7
8
## // 使用 vec 而非数组以避免引用,
## // 因为编写此示例时还没有稳定的
## // 拥有所有权的数组迭代器。
let mut one_two = vec![1, 2].into_iter();
assert_eq!(
    (1, 2),
    (one_two.next().unwrap(), one_two.next().unwrap())
);

注意 由于这是递归应用的,这些表达式也从最内层到最外层求值,在没有更内层的子表达式之前会忽略兄弟表达式。

r[expr.place-value]

位置表达式与值表达式

r[expr.place-value.intro] 表达式分为两大类:位置表达式和值表达式;还有第三类较小的范畴,称为赋值目标表达式。在每个表达式内部,操作数同样可能出现在位置上下文或值上下文中。表达式的求值既取决于它自身的类别,也取决于它所处的上下文。

r[expr.place-value.place-memory-location] 位置表达式是表示内存位置的表达式。

r[expr.place-value.place-expr-kinds] 这些表达式是引用局部变量的路径、静态变量、解引用(*expr)、数组索引表达式(expr[expr])、字段引用(expr.f)以及圆括号括起的位置表达式。

r[expr.place-value.value-expr-kinds] 所有其他表达式都是值表达式。

r[expr.place-value.value-result] 值表达式是表示实际值的表达式。

r[expr.place-value.place-context] 下列上下文是位置表达式上下文:

注意 历史上,位置表达式曾被称为 lvalue(左值),值表达式曾被称为 rvalue(右值)。

r[expr.place-value.assignee] 赋值目标表达式是出现在赋值表达式左操作数中的表达式。具体而言,赋值目标表达式是:

r[expr.place-value.parenthesis] 赋值目标表达式内部允许任意加括号。

r[expr.move]

移动与复制的类型

r[expr.move.intro] 当位置表达式在值表达式上下文中被求值,或在模式中按值绑定,它表示该内存位置_中_所持有的值。

r[expr.move.copy] 若该值的类型实现了 Copy,则该值会被复制。

r[expr.move.requires-sized] 在其余情况下,若该类型是 Sized,则可能可以移动该值。

r[expr.move.movable-place] 只有下列位置表达式可以从中移出:

r[expr.move.deinitialization] 从求值为局部变量的位置表达式中移出之后,该位置被去初始化,在重新初始化之前不能再读取。

r[expr.move.place-invalid] 在所有其他情况下,试图在值表达式上下文中使用位置表达式都是错误。

r[expr.mut]

可变性

r[expr.mut.intro] 位置表达式要被赋值、可变借用、隐式可变借用,或绑定到含 ref mut 的模式,它必须是_可变的_。我们称这些为可变位置表达式。相对地,其他位置表达式称为不可变位置表达式。

r[expr.mut.valid-places] 下列表达式可以成为可变位置表达式上下文:

  • 当前未被借用的可变变量。
  • 可变 static 项。
  • 临时值。
  • 字段:这会在可变位置表达式上下文中求值子表达式。
  • 对 *mut T 指针的解引用。
  • 对类型为 &mut T 的变量或其字段的解引用。注意:这是下一条规则要求的例外。
  • 对实现了 DerefMut 的类型的解引用:这进而要求被解引用的值在可变位置表达式上下文中求值。
  • 对实现了 IndexMut 的类型的数组索引:这会在可变位置表达式上下文中求值被索引的值,但不求值索引。

r[expr.temporary]

临时值

当在大多数位置表达式上下文中使用值表达式时,会创建一个未命名的临时内存位置,并初始化为该值。表达式求值为该位置,除非它被提升为 static。该临时值的drop 作用域通常是包围语句的末尾。

r[expr.super-macros]

超宏

r[expr.super-macros.intro] 某些内置宏可能创建其作用域可被延长的临时值。这些临时值是超临时值,这些宏是超宏。这些宏的调用是超宏调用表达式。这些宏的实参可能是超操作数。

注意 当超宏调用表达式是延长表达式时,其超操作数是延长表达式,且超临时值的作用域会被延长。见 [destructors.scope.lifetime-extension.exprs]。

r[expr.super-macros.format_args]

format_args!

r[expr.super-macros.format_args.super-operands] 除格式字符串实参外,传给 format_args! 的所有实参都是超操作数。

1
2
3
4
5
## fn temp() -> String { String::from("") }
// 由于该调用是延长表达式,且该实参是超操作数,
// 内部块也是延长表达式,因此其尾表达式中
// 创建的临时值的作用域被延长。
let _ = format_args!("{}", { &temp() }); // 正确

r[expr.super-macros.format_args.super-temporaries] format_args! 的超操作数会被隐式借用,因此是位置表达式上下文。当以值表达式作为实参传入时,会创建一个超临时值。

1
2
3
## fn temp() -> String { String::from("") }
let x = format_args!("{}", temp());
x; // <-- 临时值被延长,因而可以在此处使用。

对 format_args! 的调用展开有时会创建其他内部超临时值。

1
2
3
4
5
6
let x = {
    // 此次调用会创建一个内部临时值。
    let x = format_args!("{:?}", 0);
    x // <-- 临时值被延长,因而可以在此处使用。
}; // <-- 临时值在此处被 drop。
x; // 错误
1
2
3
// 此次调用不会创建内部临时值。
let x = { let x = format_args!("{}", 0); x };
x; // 正确

注意 format_args! 何时会或不创建内部临时值的细节目前尚未规定。

r[expr.super-macros.pin]

pin!

r[expr.super-macros.pin.super-operands] pin! 的实参是超操作数。

1
2
3
4
## use core::pin::pin;
## fn temp() {}
// 与上文 `format_args!` 的情况相同。
let _ = pin!({ &temp() }); // 正确

r[expr.super-macros.pin.super-temporaries] pin! 的实参是值表达式上下文,并创建一个超临时值。

1
2
3
4
5
6
## use core::pin::pin;
## fn temp() {}
// 该实参被求值到一个超临时值中。
let x = pin!(temp());
// 临时值被延长,因而可以在此处使用。
x; // 正确

r[expr.implicit-borrow]

隐式借用

r[expr.implicit-borrow-intro] 某些表达式会通过隐式借用把一个表达式当作位置表达式。例如,可以直接比较两个不定大小的切片是否相等,因为 == 运算符会隐式借用其操作数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
## let c = [1, 2, 3];
## let d = vec![1, 2, 3];
let a: &[i32];
let b: &[i32];
## a = &c;
## b = &d;
// ...
*a == *b;
// 等价形式:
::std::cmp::PartialEq::eq(&*a, &*b);

r[expr.implicit-borrow.application] 下列表达式中可能发生隐式借用:

r[expr.overload]

重载 trait

下列许多运算符和表达式也可以通过 std::ops 或 std::cmp 中的 trait 为其他类型重载。这些 trait 在 core::ops 和 core::cmp 中也以相同名称存在。

r[expr.attr]

表达式属性

r[expr.attr.restriction] 表达式之前的外部属性仅在少数特定情况下允许:

r[expr.attr.never-before] 它们从不允许出现在下列位置之前:

  • 区间表达式。
  • 二元运算符表达式([ArithmeticOrLogicalExpression]、[ComparisonExpression]、[LazyBooleanExpression]、[TypeCastExpression]、[AssignmentExpression]、[CompoundAssignmentExpression])。

01-字面量表达式

字面量表达式 — The Rust Reference

02-路径表达式

路径表达式 — The Rust Reference

03-块表达式

块表达式 — The Rust Reference

04-运算符表达式

运算符表达式 — The Rust Reference

05-分组表达式

分组表达式 — The Rust Reference

06-数组与索引表达式

数组与索引表达式 — The Rust Reference

07-元组与索引表达式

元组与索引表达式 — The Rust Reference

08-结构体表达式

结构体表达式 — The Rust Reference

09-调用表达式

调用表达式 — The Rust Reference

10-方法调用表达式

方法调用表达式 — The Rust Reference

11-字段访问表达式

字段访问表达式 — The Rust Reference

12-闭包表达式

闭包表达式 — The Rust Reference

13-循环表达式

循环表达式 — The Rust Reference

14-区间表达式

区间表达式 — The Rust Reference

15-if 表达式

if 表达式 — The Rust Reference

16-match 表达式

match 表达式 — The Rust Reference

17-return 表达式

return 表达式 — The Rust Reference

18-await 表达式

await 表达式 — The Rust Reference

19-下划线表达式

下划线表达式 — The Rust Reference

最后修改 August 21, 2026: 更新 (76fc81a2e)