Rust-reference/expressions
Expressions
表达式
commit: 8c4522851452563b715b11d4cd755b36d8e4bca5
本章译文最后维护日期:2020-11-11
句法
Expression :
ExpressionWithoutBlock
| ExpressionWithBlockExpressionWithoutBlock :
OuterAttribute*†
(
LiteralExpression
| PathExpression
| OperatorExpression
| GroupedExpression
| ArrayExpression
| AwaitExpression
| IndexExpression
| TupleExpression
| TupleIndexingExpression
| StructExpression
| EnumerationVariantExpression
| CallExpression
| MethodCallExpression
| FieldExpression
| ClosureExpression
| ContinueExpression
| BreakExpression
| RangeExpression
| ReturnExpression
| MacroInvocation
)ExpressionWithBlock :
OuterAttribute*†
(
BlockExpression
| AsyncBlockExpression
| UnsafeBlockExpression
| LoopExpression
| IfExpression
| IfLetExpression
| MatchExpression
)
一个表达式可能有两个角色:它总是能产生[1]一个值;它还有可能表达出效果(effects)(也被称为“副作用(side effects)”)。表达式 求值/计算为(evaluates to) 值,并在 求值(evaluation) 期间表达出效果。
许多表达式包含子表达式(操作数)。每种表达式都表达了以下几点含义:
- 在对表达式求值时是否对子表达式求值
- 对子表达式求值的顺序
- 如何组合子表达式的值来获取表达式的值
基于对这几种含义的实现要求,表达式通过其内在结构规定了其执行结构。
块只是另一种表达式,所以块、语句和表达式可以递归地彼此嵌套到任意深度。
Expression precedence
表达式的优先级
Rust 运算符和表达式的优先级顺序如下,从强到弱。具有相同优先级的二元运算符按其结合(associativity)顺序做了分组。
| 运算符/表达式 | 结合性 | |
|---|---|---|
| Paths(路径) | ||
| Method calls(方法调用) | ||
| Field expressions (字段表达式) | 从左向右 | |
| Function calls, array indexing(函数调用,数组索引) | ||
?
|
||
Unary(一元运算符) - * ! & &mut
|
||
as
|
从左向右 | |
* / %
|
从左向右 | |
+ -
|
从左向右 | |
<< >>
|
从左向右 | |
&
|
从左向右 | |
^
|
从左向右 | |
| 从左向右 | ||
== != < > <= >=
|
需要圆括号 | |
&&
|
从左向右 | |
|
从左向右 | |
.. ..=
|
需要圆括号 | |
= ^= <<= >>=
|
从右向左 | |
return break closures(返回、中断、闭包)
|
Place Expressions and Value Expressions
位置表达式和值表达式
表达式分为两大类:位置表达式和值表达式(,它们各自形成了自己的上下文)。因此,在每个表达式中,子表达式可以出现在位置上下文,也可出现在值上下文中。表达式的求值既依赖于它自己的类别,也依赖于它所在的上下文。
位置表达式是表示内存位置的表达式。语言中表现为指向局部变量、静态变量、解引用(*expr)、数组索引表达式(expr[expr])、字段引用(expr.f)、圆括号括起来的位置表达式的路径。所有其他形式的表达式都是值表达式。
值表达式是代表实际值的表达式。
下面的上下文是位置表达式上下文:
- 赋值或复合赋值表达式的左操作数。
- 一元运算符借用或解引用的操作数。
- 字段表达式的操作数。
- 数组索引表达式的被索引操作数。
- 任何隐式借用的操作数。
- let语句的初始化器(initializer)。
if let表达式、while let表达式或匹配(match)表达式的检验对象(scrutinee)。- 结构体表达式里的函数式更新(functional update)的基(base)。
注意:历史上,位置表达式被称为 左值/lvalues,值表达式被称为 右值/rvalues。
Moved and copied types
移动语义类型和复制语义类型
当位置表达式在值表达式上下文中被求值时,或在模式中被值绑定时,这表示此值会保存进(held in)当前表达式代表的内存地址。如果该值的类型实现了 Copy,那么该值将被从原来的位置复制一份过来。如果该值的类型没有实现 Copy,但实现了 Sized,那么就有可能把该值从原来的位置移动(move)过来。从位置表达式里移出值对位置表达式也有要求,只有如下的位置表达式才可能把值从其中移出(move out):
- 变量当前未被借用。
- 临时值(Temporary values)。
- 可以移出且没实现
Drop的位置表达式的字段。 - 对可移出且类型为 [[../../std/boxed/struct.Box|
Box<T>]] 的表达式作解引用的结果。
把值从一个位置表达式里移出到一个局部变量,那此(表达式代表的)地址将被去初始化(deinitialized),并且该地址在重新初始化之前无法被再次读取。
除以上列出的情况外,任何在值表达式上下文中使用位置表达式都是错误的。
Mutability
可变性
如果一个位置表达式将会被赋值、可变借出、隐式可变借出或被绑定到包含 ref mut 模式上,则该位置表达式必须是可变的(mutable)。我们称这类位置表达式为可变位置表达式。与之相对,其他位置表达式称为不可变位置表达式。
下面的表达式可以是可变位置表达式上下文:
- 当前未被借出的可变变量。
- 可变静态(
static)项。 - 临时值。
- 字段,在可变位置表达式上下文中,可以对此子表达式求值。
- 对
*mut T指针的解引用。 - 对类型为
&mut T的变量或变量的字段的解引用。注意:这条是下一条规则的必要条件的例外情况。[2] - 对实现
DerefMut的类型的解引用,那对这里解出的表达式求值就需要在一个可变位置表达式上下文中进行。 - 对实现
IndexMut的类型做索引,那对此检索出的表达式求值就需要在一个可变位置表达式上下文进行。注意对索引(index)本身的求值不用。
Temporaries
临时位置/临时变量
在大多数位置表达式上下文中使用值表达式时,会创建一个临时的未命名内存位置,并将该位置初始为该值,然后这个表达式的求值结果就为该内存位置。此过程也有例外,就是把此临时表达式提升为一个静态项(static)。(译者注:这种情况下表达式将直接在编译时就求值了,求值的结果会根据编译器要求重新选择地址存储)。临时位置/临时变量的销毁作用域(drop scope)通常在包含它的最内层语句的结尾处。
Implicit Borrows
隐式借用
某些特定的表达式可以通过隐式借用一个表达式来将其视为位置表达式。例如,可以直接比较两个非固定尺寸的切片是否相等,因为 == 操作符隐式借用了它自身的操作数:
# let c = [1, 2, 3];
# let d = vec![1, 2, 3];
let a: &[i32];
let b: &[i32];
# a = &c;
# b = &d;
// ...
*a == *b; //译者注:&[i32] 解引用后是一个动态尺寸类型,理论上两个动态尺寸类型上无法比较大小的,但这里因为隐式借用此成为可能
// 等价于下面的形式:
::std::cmp::PartialEq::eq(&*a, &*b);
隐式借用可能会被以下表达式采用:
- 方法调用表达式中的左操作数。
- 字段表达式中的左操作数。
- 调用表达式中的左操作数。
- 数组索引表达式中的左操作数。
- 解引用操作符(
*)的操作数。 - 比较运算的操作数。
- 复合赋值(compound assignment)的左操作数。
Overloading Traits
重载trait
本书后续章节的许多操作符和表达式都可以通过使用 std::ops 或 std::cmp 中的 trait 被其他类型重载。这些 trait 也存在于同名的 core::ops 和 core::cmp 中。
Expression Attributes
表达式属性
只有在少数特定情况下,才允许在表达式之前使用外部属性:
- 在被当作语句用的表达式之前。
- 数组表达式、元组表达式、调用表达式、元组结构体(tuple-style struct)表达式、枚举变体 表达式这些中的元素。
- 块表达式的尾部表达式(tail expression)。
在下面情形之前是不允许的: