块表达式
语法
BlockExpression :
{
内部属性*
语句?
}
一个 块表达式,或者简称 块,是一种控制流表达式,同时也是用于项(item)和变量声明的匿名命名空间作用域。
作为一个控制流表达式,块表达式按顺序执行其非项声明的语句,然后执行其末尾的可选表达式。
作为匿名命名空间作用域,项声明只在块内部可见,而由 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
? 块表达式
一个 async 块 是块表达式的一种变体,它评估为一个 future。
如果存在,块的最终表达式决定了 future 的结果值。
执行 async 块类似于执行闭包表达式:它的直接效果是产生并返回一个匿名类型。
然而,与闭包返回实现一个或多个 std::ops::Fn
特征的类型不同,async 块返回的类型实现 std::future::Future
特征。
该类型的实际数据布局是未指定的。
注意
rustc 生成的 future 类型大致等价于一个枚举,每个
await
点有一个变体,每个变体存储了从其对应点恢复所需的数据。
版本差异: Async 块仅在 Rust 2018 及更高版本中可用。
捕获模式
Async 块使用与闭包相同的捕获模式从环境中捕获变量。与闭包类似,当写成 async { .. }
时,每个变量的捕获模式将从块的内容推断出来。然而,async move { .. }
块会将所有引用的变量移动到结果 future 中。
Async 上下文
由于 async 块构造了一个 future,它们定义了一个 async 上下文,该上下文又可以包含 await
表达式。Async 上下文由 async 块以及 async 函数体建立,它们的语义定义为基于 async 块。
控制流操作符
Async 块就像函数边界一样,非常类似于闭包。
因此,?
操作符和 return
表达式都影响 future 的输出,而不是包含的函数或其他上下文。也就是说,在 async 块中使用 return <expr>
将把 <expr>
的结果作为 future 的输出返回。同样,如果 <expr>?
传播错误,该错误将作为 future 的结果传播。
最后,break
和 continue
关键字不能用于从 async 块跳出。因此,以下代码是非法的:
#![allow(unused)] fn main() { loop { async move { break; // error[E0267]: `break` inside of an `async` block } } }
const
块
语法
ConstBlockExpression :
const
块表达式
一个 const 块 是块表达式的一种变体,其主体在编译时而不是运行时评估。
Const 块允许你在不定义新的常量项的情况下定义一个常量值,因此它们有时也被称为 内联常量。它也支持类型推断,所以不像常量项那样无需指定类型。
Const 块能够引用作用域内的泛型参数,这与自由常量项不同。它们被解糖为带作用域内泛型参数的常量项(类似于关联常量,但没有关联的特征或类型)。例如,这段代码:
#![allow(unused)] fn main() { fn foo<T>() -> usize { const { std::mem::size_of::<T>() + 1 } } }
等价于:
#![allow(unused)] fn main() { fn foo<T>() -> usize { { struct Const<T>(T); impl<T> Const<T> { const CONST: usize = std::mem::size_of::<T>() + 1; } Const::<T>::CONST } } }
如果在运行时执行 const 块表达式,则保证常量会被评估,即使其返回值被忽略。
#![allow(unused)] fn main() { fn foo<T>() -> usize { // If this code ever gets executed, then the assertion has definitely // been evaluated at compile-time. const { assert!(std::mem::size_of::<T>() > 0); } // Here we can have unsafe code relying on the type being non-zero-sized. /* ... */ 42 } }
如果 const 块表达式在运行时未执行,则可能评估也可能不评估。
#![allow(unused)] fn main() { if false { // The panic may or may not occur when the program is built. const { panic!(); } } }
unsafe
块
语法
UnsafeBlockExpression :
unsafe
块表达式
有关何时使用 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 } } }