表达式
语法
表达式 :
无块表达式
| 块表达式无块表达式 :
外围属性*†
(
字面值表达式
| 路径表达式
| 操作符表达式
| 分组表达式
| 数组表达式
| Await表达式
| 索引表达式
| 元组表达式
| 元组索引表达式
| 结构体表达式
| 调用表达式
| 方法调用表达式
| 字段表达式
| 闭包表达式
| Async块表达式
| Continue表达式
| Break表达式
| 区间表达式
| Return表达式
| 下划线表达式
| 宏调用表达式
)块表达式 :
外围属性*†
(
块表达式
| Unsafe块表达式
| Loop表达式
| If表达式
| IfLet表达式
| Match表达式
)
表达式具有两个作用: 总是产生一个值,并可能产生其他效果 (也称为 "副作用" )。 副作用在表达式计算为一个值的期间产生。 许多表达式包含子表达式,也称为表达式的操作数。表达式的具体语义决定了以下行为:
- 在计算表达式时是否进一步计算操作数
- 计算操作数的顺序
- 如何将操作数的值组合以获得表达式的值
块仍是表达式的一种,因此块、语句、表达式和块可以递归嵌套。
注意: 给表达式的操作数命名可以便于讨论,但目前这些名称未稳定,可能会更改。
表达式优先级
Rust 运算符和表达式的优先级按照以下顺序进行排列,从强到弱。 在相同优先级的二元运算符中,根据其结合性得出分组顺序。
运算符/表达式 | 结合性 |
---|---|
路径 | |
方法调用 | |
字段表达式 | 从左到右 |
函数调用、数组索引 | |
? | |
一元 - * ! & &mut | |
as | 从左到右 |
* / % | 从左到右 |
+ - | 从左到右 |
<< >> | 从左到右 |
& | 从左到右 |
^ | 从左到右 |
| | 从左到右 |
== != < > <= >= | 需要括号 |
&& | 从左到右 |
|| | 从左到右 |
.. ..= | 需要括号 |
= += -= *= /= %= &= |= ^= <<= >>= | 从右到左 |
return break 闭包 |
操作数的求值顺序
以下表达式列表的所有表达式都以相同的方式对其操作数进行计算。 其他表达式要么不需要操作数,要么根据其各自的语法进行有条件的计算。
- 解引用表达式
- 错误传导表达式
- 取反表达式
- 算术和逻辑二元运算符
- 比较运算符
- 类型转换表达式
- 分组表达式
- 数组表达式
await
表达式- 索引表达式
- 元组表达式
- 元组索引表达式
- 结构体表达式
- 函数调用表达式
- 方法调用表达式
- 字段表达式
break
表达式- 范围表达式
return
表达式
这些表达式的操作数在产生表达式副作用之前进行求值。 取多个操作数的表达式按照源代码中从左到右的顺序进行求值。
注意: 其子表达式是否为一个表达式的操作数由前面的 "表达式优先级" 决定。
例如,两个 next
方法调用总是按照它们在代码中出现的顺序被调用:
#![allow(unused)] fn main() { // 在这个例子中使用 `Vec` 而不是数组,是为了避免引用,因为在编写此示例时,数组的迭代器特性还没有稳定。 let mut one_two = vec![1, 2].into_iter(); assert_eq!( (1, 2), (one_two.next().unwrap(), one_two.next().unwrap()) ); }
注意: 由于这个规则是递归的,所以这些表达式从内到外依次求值,直到没有更深层次的子表达式。在这个过程中会暂时忽略同级的表达式。
占位表达式和值表达式
表达式从求值的特性上可区分为: 占位表达式和值表达式,以及次要的可赋值表达式。 在表达式中,操作数可以出现在占位上下文中或值上下文中。
占位表达式 是表示内存地址的表达式。这些表达式以 路径 引用局部变量、 静态变量 、 解引用 、 数组索引 表达式 ( expr[expr]
) 、 字段 引用 (expr.f
) 和带括号的占位表达式。其他表达式都是值表达式。
值表达式 是表示真实值的表达式。
以下为占位表达式上下文:
- 复合赋值 表达式的左操作数。
- 单目 借用 、 取地址 或 解引用 操作符的操作数。
- 字段表达式的操作数。
- 数组索引表达式的索引操作数。
- 任何 隐式借用 的操作数。
- let 语句 的初始化器。
if let
、match
或while let
表达式的 被匹配项 。- 结构体函数式更新 表达式的基础。
译注: 基础这个概念在多处被使用,类似于基类,但 rust 中没传统继承这概念,使用基类将可能引起读者混淆。
注意: 从历史上看,占位表达式称为左值,值表达式称为右值。
可赋值表达式 是指出现在 赋值 表达式的左操作数的表达式。 确切来说,可赋值表达式可以是:
- 占位表达式
- 下划线表达式。
- 包含可赋值表达式的 元组表达式。
- 包含可赋值表达式的 数组表达式。
- 包含可赋值表达式的 元组结构体表达式。
- 包含可赋值表达式的 结构体表达式 (可包含命名字段) 。
- 单元结构体。
在可赋值表达式中允许使用任意括号。
移动和复制类型
当在值表达式上下文中计算占位表达式,或者通过值在模式中绑定占位表达式时,它表示该内存位置所存储的值。
如果该值的类型实现了 Copy
,那么该值将被复制。
其它情况,如果该类型是 Sized
,那么可能移动该值。
只有以下占位表达式可以 '移出' :
在计算时从局部变量的占位表达式中移动出后,该占位将被取消初始化,不能再次读取,直到重新初始化。 在其他情况,尝试在值表达式上下文中使用占位表达式将产生错误。
Mutability
要将一个占位表达式赋值、进行可变借用、进行隐式可变借用或绑定到包含 ref mut
的模式中时,则必须是可变的。
这被称为可变占位表达式,其他称为不可变占位表达式。
下面的表达式可以是可变占位表达式上下文:
- 当前没有被借用的可变 变量 。
- 可变
static
条目 。 - 临时值 。
- 字段: 这将在可变占位表达式上下文中计算子表达式。
*mut T
指针的 解引用 。- 类型为
&mut T
的变量或变量字段的解引用。注意:这是对下一条规则的例外情况。 - 实现
DerefMut
的类型的解引用: 这要求被解引用的值在可变占位表达式上下文中求值。 - 实现
IndexMut
的类型的 数组索引: 这将在可变占位表达式上下文中计算被索引的值,但不包括索引。
临时值
在大多数占位表达式上下文中使用值表达式时,会创建一个临时的无名内存位置,并将其初始化为该值。
表达式计算为该位置,除非它被 提升 为 static
静态的。
临时值的 丢弃作用域 通常是封闭语句的末尾。
隐式借用
某些表达式将会通过隐式借用将一个表达式作为占位表达式。
比如,可以直接比较两个非固定大小的 切片 是否相等,因为 ==
运算符会隐式地借用它的操作数:
#![allow(unused)] fn main() { 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); }
隐式借用可以出现在以下表达式中:
trait 重载
许多以下的运算符和表达式可以使用 std::ops
或 std::cmp
中的 trait 重载为其他类型可用。
这些 trait 在 core::ops
和 core::cmp
中也有相同的名称。
表达式属性
在表达式前面的 外围属性 只允许在以下几种情况下使用:
它们不允许在以下情况下使用: