Block expressions
块表达式
commit: 6b90080371ff44d0074a465945dfdb0de4b50774
本章译文最后维护日期:2020-11-11
句法
BlockExpression :
{
InnerAttribute*
Statements?
}
Statements :
Statement+
| Statement+ ExpressionWithoutBlock
| ExpressionWithoutBlock
块表达式或块是一个控制流表达式(control flow expression),同时也是程序项声明和变量声明的匿名空间作用域。作为控制流表达式,块按顺序执行其非程序项声明的语句组件,最后执行可选的最终表达式(final expression)。作为一个匿名空间作用域,在本块内声明的程序项只在块本身围成的作用域内有效,而块内由 let
语句声明的变量的作用域为下一条语句到块尾。
块的书写形式为:先是一个 {
,然后是内部属性,再后是各条语句,再后是一个可选表达式,最后是一个 }
。语句之间通常需要后跟分号,但有两个例外。程序项声明语句不需要后跟分号;表达式语句通常需要后面的分号,但它的外层表达式是控制流表达式时不需要。此外,允许在语句之间使用额外的分号,但是这些分号并不影响语义。
在对块表达式求值时,除了程序项声明语句外,每个语句都是按顺序执行的。如果给出了块尾的可选的最终表达式(final expression),则最后会执行它。
块的类型是最此块的最终表达式(final expression)的类型,但如果省略了最终表达式,则块的类型为 ()
。
# fn fn_call() {}
let _: () = {
fn_call();
};
let five: i32 = {
fn_call();
5
};
assert_eq!(5, five);
注意:作为控制流表达式,如果块表达式是一个表达式语句的外层表达式,则该块表达式的预期类型为
()
,除非该块后面紧跟着一个分号。
块总是值表达式,并会在值表达式上下文中对最终表达式求值。如果确实有需要,块可以用于强制移动值。例如,下面的示例在调用 consume_self
时失败,因为结构体已经在之前的块表达式里被从 s
里移出了。
struct Struct; impl Struct { fn consume_self(self) {} fn borrow_self(&self) {} } fn move_by_block_expression() { let s = Struct; // 将值从块表达式里的 `s` 里移出。 (&{ s }).borrow_self(); // 执行失败,因为 `s` 里的值已经被移出。 s.consume_self(); }
async
blocks
async
块
句法
AsyncBlockExpression :
async
move
? BlockExpression
异步块(async block)是求值为 future 的块表达式的一个变体。块的最终表达式(如果存在)决定了 future 的结果值。(译者注:单词 future 对应中文为“未来”。原文可能为了双关的行文效果,经常把作为类型的 future 和字面意义上的 future 经常混用,所以译者基本保留此单词不翻译,特别强调“未来”的意义时也会加上其英文单词。)
执行一个异步块类似于执行一个闭包表达式:它的即时效果是生成并返回一个匿名类型。类似闭包返回的类型实现了一个或多个 std::ops::Fn
trait,异步块返回的类型实现了 std::future::Future
trait。此类型的实际数据格式规范还未确定下来。
注意: rustc 生成的 future类型大致相当于一个枚举,rustc 为这个 future 的每个
await
点生成一个此枚举的变体,其中每个变体都存储了对应点再次恢复执行时需要的数据。
版本差异: 异步块从 Rust 2018 版才开始可用。
Capture modes
捕获方式
异步块使用与闭包相同的捕获方式从其环境中捕获变量。跟闭包一样,当编写 async { .. }
时,每个变量的捕获方式将从该块里的内容中推断出来。
而 async move { .. }
类型的异步块将把所有需要捕获的变量使用移动语义移入(move)到相应的结果 future 中。
Async context
异步上下文
因为异步块构造了一个 future,所以它们定义了一个async上下文,这个上下文可以相应地包含 await
表达式。异步上下文是由异步块和异步函数的函数体建立的,它们的语义是依照异步块定义的。
Control-flow operators
控制流操作符
异步块的作用类似于函数边界,或者更类似于闭包。因此 ?
操作符和 返回(return
)表达式也都能影响 future 的输出,且不会影响封闭它的函数或其他上下文。也就是说,future 的输出跟闭包将其中的 return <expr>
的表达式 <expr>
的计算结果作为未来的输出的做法是一样的。类似地,如果 <expr>?
传播(propagate)一个错误,这个错误也会被 future 在未来的某个时候作为返回结果被传播出去。
最后,关键字 break
和 continue
不能用于从异步块中跳出分支。因此,以下内容是非法的:
loop { async move { break; // 这将打破循环。 } }
unsafe
blocks
非安全(unsafe
)块
句法
UnsafeBlockExpression :
unsafe
BlockExpression
查看 unsafe
块以了解更多该何时使用unsafe
的信息
可以在代码块前面加上关键字 unsafe
以允许非安全操作。例如:
unsafe {
let b = [13u8, 17u8];
let a = &b[0] as *const u8;
assert_eq!(*a, 13);
assert_eq!(*a.offset(1), 17);
}
# unsafe fn an_unsafe_fn() -> i32 { 10 }
let a = unsafe { an_unsafe_fn() };
Attributes on block expressions
块表达式上的属性
在以下上下文中,允许在块表达式的左括号之后直接使用内部属性:
- 函数和方法的代码体。
- 循环体(
loop
,while
,while let
, 和for
)。 - 被用作语句的块表达式。
- 块表达式作为数组表达式、元组表达式、调用表达式、元组结构体表达式和枚举变体表达式的元素。
- 作为另一个块表达式的尾部表达式(tail expression)的块表达式。
在块表达式上有意义的属性有 cfg
和 lint检查类属性。
例如,下面这个函数在 unix 平台上返回 true
,在其他平台上返回 false
。
fn is_unix_platform() -> bool {
#[cfg(unix)] { true }
#[cfg(not(unix))] { false }
}