块表达式
Syntax
BlockExpression →
{
InnerAttribute*
Statements?
}
Statements →
Statement+
| Statement+ ExpressionWithoutBlock
| ExpressionWithoutBlock
一个 块表达式 ,或称 块 ,是一个控制流表达式,也是 项 和变量声明的匿名命名空间作用域。
作为控制流表达式,块会顺序执行其组成的非 项 声明语句,然后执行其最后的可选表达式。
作为匿名命名空间作用域, 项 声明仅在块内部有效,而由 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; // 在块表达式中将值从 `s` 中移出。 (&{ s }).borrow_self(); // 执行失败,因为 `s` 已被移出。 s.consume_self(); } }
async块
Syntax
AsyncBlockExpression → async move? BlockExpression
一个 async 块 是块表达式的一种变体,其求值结果为一个 future。
块的最终表达式(如果存在)决定了 future 的结果值。
执行 async 块类似于执行闭包表达式: 它的直接效果是产生并返回一个匿名类型。
然而,闭包返回实现了一个或多个 std::ops::Fn 特型 的类型,而 async 块返回的类型实现了 std::future::Future 特型 。
该类型的实际数据格式未指定。
注意
rustc 生成的 future 类型大致相当于一个枚举,每个
await点对应一个变体,每个变体存储从其对应点恢复所需的数据。
2018 版次差异
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]: 在 `async` 块内部使用 `break`
}
}
}
const块
Syntax
ConstBlockExpression → const BlockExpression
一个 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 {
// 如果这段代码被执行,那么断言肯定已经在编译时求值了。
const { assert!(std::mem::size_of::<T>() > 0); }
// 在这里,我们可以编写依赖于类型为非零大小的 unsafe 代码。
/* ... */
42
}
}
如果 const 块表达式不在运行时执行,它可能会也可能不会被求值:
#![allow(unused)]
fn main() {
if false {
// 程序构建时可能会也可能不会发生 恐慌 。
const { panic!(); }
}
}
unsafe块
Syntax
UnsafeBlockExpression → unsafe BlockExpression
_ 有关何时使用 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和for)。 - 用作 语句 的块表达式。
- 作为 数组表达式 、 元组表达式 、 调用表达式 和元组风格 结构体 表达式元素的块表达式。
- 作为另一个块表达式的尾随表达式的块表达式。
在块表达式上有意义的属性是 cfg 和 lint 检查属性 。
例如,此函数在 unix 平台上返回 true ,在其他平台上返回 false 。
#![allow(unused)]
fn main() {
fn is_unix_platform() -> bool {
#[cfg(unix)] { true }
#[cfg(not(unix))] { false }
}
}