表达式

语法

表达式 :

      无块表达式

   | 带块表达式

无块表达式 :

   外部属性*

   (

         字面量表达式

      | 路径表达式

      | 运算符表达式

      | 分组表达式

      | 数组表达式

      | Await 表达式

      | 索引表达式

      | 元组表达式

      | 元组索引表达式

      | 结构体表达式

      | 调用表达式

      | 方法调用表达式

      | 字段表达式

      | 闭包表达式

      | 异步块表达式

      | Continue 表达式

      | Break 表达式

      | 范围表达式

      | Return 表达式

      | 下划线表达式

      | 宏调用

   )

带块表达式 :

   外部属性*

   (

         块表达式

      | 不安全块表达式

      | 循环表达式

      | If 表达式

      | If Let 表达式

      | Match 表达式

   )

表达式可以有两个作用:它总是产生一个,并且它可能具有效果(也称为“副作用”)。表达式求值为一个值,并在求值期间产生效果。许多表达式包含子表达式,称为表达式的操作数。每种表达式的含义都规定了以下几点

  • 在对表达式求值时是否对操作数求值
  • 对操作数求值的顺序
  • 如何组合操作数的值以获得表达式的值

这样,表达式的结构就决定了执行的结构。代码块只是另一种表达式,因此代码块、语句、表达式和代码块可以递归地相互嵌套到任意深度。

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

表达式优先级

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

运算符/表达式结合性
路径
方法调用
字段表达式从左到右
函数调用、数组索引
?
一元运算符 - * ! & &mut
as从左到右
* / %从左到右
+ -从左到右
<< >>从左到右
&从左到右
^从左到右
|从左到右
== != < > <= >=需要括号
&&从左到右
||从左到右
.. ..=需要括号
= += -= *= /= %=

&= |= ^= <<= >>=
从右到左
return break 闭包

操作数的求值顺序

以下表达式列表都以相同的方式对其操作数进行求值,如列表后所述。其他表达式要么不带操作数,要么根据其各自页面上的描述有条件地对其进行求值。

  • 解引用表达式
  • 错误传播表达式
  • 取反表达式
  • 算术和逻辑二元运算符
  • 比较运算符
  • 类型转换表达式
  • 分组表达式
  • 数组表达式
  • Await 表达式
  • 索引表达式
  • 元组表达式
  • 元组索引表达式
  • 结构体表达式
  • 调用表达式
  • 方法调用表达式
  • 字段表达式
  • Break 表达式
  • 范围表达式
  • Return 表达式

在应用表达式的影响之前,会先对这些表达式的操作数进行求值。接受多个操作数的表达式按源代码中编写的顺序从左到右求值。

注意:哪些子表达式是表达式的操作数由上一节中表达式优先级确定。

例如,两个 next 方法调用将始终按相同的顺序调用

#![allow(unused)]
fn main() {
// Using vec instead of array to avoid references
// since there is no stable owned array iterator
// at the time this example was written.
let mut one_two = vec![1, 2].into_iter();
assert_eq!(
    (1, 2),
    (one_two.next().unwrap(), one_two.next().unwrap())
);
}

注意:由于这是递归应用的,因此这些表达式也会从最内层到最外层进行求值,忽略同级表达式,直到没有内部子表达式为止。

位置表达式和值表达式

表达式分为两大类:位置表达式和值表达式;还有第三种次要的表达式类别,称为赋值表达式。在每个表达式中,操作数同样可以出现在位置上下文或值上下文中。表达式的求值取决于其自身类别和它出现的上下文。

位置表达式是表示内存位置的表达式。这些表达式是引用局部变量的路径静态变量解引用 (*expr)、数组索引表达式 (expr[expr])、字段引用 (expr.f) 和带括号的位置表达式。所有其他表达式都是值表达式。

值表达式是表示实际值的表达式。

以下上下文是位置表达式上下文

注意:过去,位置表达式被称为左值,值表达式被称为右值

赋值表达式是出现在赋值表达式左操作数中的表达式。明确地说,赋值表达式是

赋值表达式内部允许任意加括号。

移动和复制类型

当在值表达式上下文中对位置表达式求值,或在模式中按值绑定时,它表示该内存位置保存的值。如果该值的类型实现了Copy,则该值将被复制。在其余情况下,如果该类型是Sized,则可以移动该值。只有以下位置表达式可以移出

从求值为局部变量的位置表达式移出后,该位置将被取消初始化,并且在重新初始化之前无法再次读取。在所有其他情况下,尝试在值表达式上下文中使用位置表达式都是错误的。

可变性

要对位置表达式进行赋值、可变地借用隐式可变地借用,或绑定到包含 ref mut 的模式,它必须是可变的。我们称这些为可变位置表达式。相反,其他位置表达式称为不可变位置表达式

以下表达式可以是可变位置表达式上下文

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

临时值

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

隐式借用

某些表达式会通过隐式借用表达式将其视为位置表达式。例如,可以直接比较两个未确定大小的切片是否相等,因为 == 运算符会隐式借用其操作数

#![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;
// Equivalent form:
::std::cmp::PartialEq::eq(&*a, &*b);
}

可以在以下表达式中进行隐式借用

运算符重载

以下许多运算符和表达式也可以使用 std::opsstd::cmp 中的特征为其他类型重载。这些特征也存在于 core::opscore::cmp 中,名称相同。

表达式属性

表达式之前的外部属性仅在少数特定情况下允许使用

它们永远不允许出现在以下位置之前