Rust-reference/expressions

来自菜鸟教程
跳转至:导航、​搜索

Expressions

表达式

expressions.md

commit: 8c4522851452563b715b11d4cd755b36d8e4bca5
本章译文最后维护日期:2020-11-11

句法

Expression :
      ExpressionWithoutBlock
   | ExpressionWithBlock

ExpressionWithoutBlock :
   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)、圆括号括起来的位置表达式路径。所有其他形式的表达式都是值表达式。

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

下面的上下文是位置表达式上下文:

注意:历史上,位置表达式被称为 左值/lvalues,值表达式被称为 右值/rvalues

Moved and copied types

移动语义类型和复制语义类型

当位置表达式在值表达式上下文中被求值时,或在模式中被值绑定时,这表示此值会保存进(held in)当前表达式代表的内存地址。如果该值的类型实现了 Copy,那么该值将被从原来的位置复制一份过来。如果该值的类型没有实现 Copy,但实现了 Sized,那么就有可能把该值从原来的位置移动(move)过来。从位置表达式里移出值对位置表达式也有要求,只有如下的位置表达式才可能把值从其中移出(move out):

把值从一个位置表达式里移出到一个局部变量,那此(表达式代表的)地址将被去初始化(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);

隐式借用可能会被以下表达式采用:

Overloading Traits

重载trait

本书后续章节的许多操作符和表达式都可以通过使用 std::opsstd::cmp 中的 trait 被其他类型重载。这些 trait 也存在于同名的 core::opscore::cmp 中。

Expression Attributes

表达式属性

只有在少数特定情况下,才允许在表达式之前使用外部属性

在下面情形之前是不允许的:

  1. 通俗理解“求值”,可以理解为:一方面可以从表达式求出值,另一方面可以为表达式赋值。
  2. 译者暂时还未用代码测试这种例外情况。