块表达式
语法
BlockExpression :
{
内部属性*
语句?
}
块表达式,或称块,是一种控制流表达式,也是项目和变量声明的匿名命名空间作用域。作为控制流表达式,块按顺序执行其组件非项目声明语句,然后执行其最终的可选表达式。作为匿名命名空间作用域,项目声明仅在块本身内部有效,而由 let
语句声明的变量从下一条语句到块结束都有效。
块的语法是 {
,然后是任意数量的 内部属性,然后是任意数量的 语句,然后是一个可选的表达式,称为最终操作数,最后是 }
。
语句后面通常需要加分号,但有两种例外情况
- 项目声明语句后面不需要加分号。
- 表达式语句通常需要在其外部表达式是控制流表达式的情况下,才需要在其后加分号。
此外,语句之间允许使用额外的分号,但这些分号不会影响语义。
在计算块表达式时,将按顺序执行每个语句,但项目声明语句除外。然后,如果给出了最终操作数,则执行该操作数。
块的类型是最终操作数的类型,如果省略了最终操作数,则为 ()
。
#![allow(unused)] fn main() { fn fn_call() {} let _: () = { fn_call(); }; let five: i32 = { fn_call(); 5 }; assert_eq!(5, five); }
注意:作为控制流表达式,如果块表达式是表达式语句的外部表达式,则除非其后紧跟分号,否则预期类型为
()
。
块始终是 值表达式,并在值表达式上下文中计算最后一个操作数。
注意:如果确实需要,可以使用此方法强制移动值。例如,以下示例在调用
consume_self
时失败,因为结构体已在块表达式中移出s
。#![allow(unused)] fn main() { struct Struct; impl Struct { fn consume_self(self) {} fn borrow_self(&self) {} } fn move_by_block_expression() { let s = Struct; // Move the value out of `s` in the block expression. (&{ s }).borrow_self(); // Fails to execute because `s` is moved out of. s.consume_self(); } }
async
块
语法
AsyncBlockExpression :
async
move
? BlockExpression
异步块是块表达式的一种变体,它计算结果为一个 future。块的最终表达式(如果存在)决定 future 的结果值。
执行异步块类似于执行闭包表达式:它的直接效果是生成并返回一个匿名类型。然而,闭包返回实现一个或多个 std::ops::Fn
特征的类型,而为异步块返回的类型实现 std::future::Future
特征。此类型的实际数据格式未指定。
注意: rustc 生成的 future 类型大致相当于一个枚举,每个
await
点对应一个变体,其中每个变体存储从其对应点恢复所需的数据。
版本差异:异步块仅从 Rust 2018 开始可用。
捕获模式
异步块使用与闭包相同的 捕获模式 从其环境中捕获变量。与闭包一样,当编写为 async { .. }
时,将从块的内容推断每个变量的捕获模式。但是,async move { .. }
块会将所有引用的变量移动到结果 future 中。
异步上下文
因为异步块构造了一个 future,所以它们定义了一个异步上下文,该上下文又可以包含 await
表达式。异步上下文由异步块以及异步函数体建立,其语义根据异步块定义。
控制流运算符
异步块的作用类似于函数边界,很像闭包。因此,?
运算符和 return
表达式都会影响 future 的输出,而不是封闭函数或其他上下文。也就是说,异步块中的 return <expr>
将返回 <expr>
的结果作为 future 的输出。类似地,如果 <expr>?
传播错误,则该错误将作为 future 的结果传播。
最后,break
和 continue
关键字不能用于从异步块中跳转。因此,以下代码是非法的
#![allow(unused)] fn main() { loop { async move { break; // error[E0267]: `break` inside of an `async` block } } }
unsafe
代码块
语法
UnsafeBlockExpression :
unsafe
BlockExpression
有关何时使用 unsafe
的更多信息,请参阅unsafe
代码块
代码块可以用 unsafe
关键字作为前缀,以允许不安全操作。例子
#![allow(unused)] fn main() { 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() }; }
带标签的代码块表达式
带标签的代码块表达式在循环和其他可中断表达式部分有详细说明。
代码块表达式上的属性
在以下情况下,内部属性允许直接放在代码块表达式的左大括号之后
- 函数和方法体。
- 循环体(
loop
、while
、while let
和for
)。 - 用作语句的代码块表达式。
- 作为数组表达式、元组表达式、调用表达式和元组风格的结构体表达式的元素的代码块表达式。
- 作为另一个代码块表达式的尾部表达式的代码块表达式。
例如,此函数在 Unix 平台上返回 true
,在其他平台上返回 false
。
#![allow(unused)] fn main() { fn is_unix_platform() -> bool { #[cfg(unix)] { true } #[cfg(not(unix))] { false } } }