块表达式

语法
BlockExpression :
   {
      InnerAttribute*
      Statements?
   }

语句 :
      Statement+
   | Statement+ ExpressionWithoutBlock
   | ExpressionWithoutBlock

块表达式,或称作,是一种控制流表达式和用于条目与变量声明的匿名命名空间作用域。作为一种控制流表达式,块会顺序执行其组成部分的非条目声明语句,然后执行其最终的可选表达式。作为一个匿名命名空间作用域,条目声明仅在块自身内部的作用域中,而由 let 语句声明的变量的作用域从下一条语句开始,直到块的末尾。有关更多详细信息,请参阅 作用域 章节。

块的语法是 {,然后是任意数量的 内部属性,然后是任意数量的 语句,然后是一个可选的表达式(称为最终操作数),最后是 }

语句通常需要在后面跟一个分号,但有两个例外:

  1. 条目声明语句不需要在后面跟分号。
  2. 表达式语句通常需要在后面跟分号,除非其外部表达式是控制流表达式。

此外,语句之间允许有多余的分号,但这些分号不会影响语义。

当评估块表达式时,每条语句(条目声明语句除外)都会按顺序执行。然后,如果给定了最终操作数,则执行它。

块的类型是最终操作数的类型,如果省略了最终操作数,则为 ()

#![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

async 块是块表达式的一种变体,它会评估为一个 future。块的最终表达式(如果存在)决定了 future 的结果值。

执行 async 块类似于执行闭包表达式:它的直接效果是生成并返回一个匿名类型。然而,闭包返回的类型实现了 std::ops::Fn traits 中的一个或多个,而 async 块返回的类型实现了 std::future::Future trait。此类型的实际数据格式未指定。

注意: rustc 生成的 future 类型大致等同于一个枚举,每个 await 点有一个变体,每个变体存储从其对应点恢复所需的数据。

版本差异:Async 块仅从 Rust 2018 开始可用。

捕获模式

Async 块使用与闭包相同的 捕获模式 从其环境中捕获变量。与闭包类似,当写成 async { .. } 时,每个变量的捕获模式将从块的内容中推断出来。然而,async move { .. } 块会将所有引用的变量移动到结果 future 中。

Async 上下文

由于 async 块构造了一个 future,它们定义了一个 async 上下文,该上下文反过来可以包含 await 表达式。Async 上下文由 async 块以及 async 函数的主体建立,async 函数的语义是根据 async 块定义的。

控制流运算符

Async 块的行为类似于函数边界,很像闭包。因此,? 运算符和 return 表达式都会影响 future 的输出,而不是封闭函数或其他上下文。也就是说,来自 async 块内的 return <expr> 将返回 <expr> 的结果作为 future 的输出。同样,如果 <expr>? 传播错误,则该错误将作为 future 的结果传播。

最后,breakcontinue 关键字不能用于从 async 块中分支出来。因此,以下代码是非法的:

#![allow(unused)]
fn main() {
loop {
    async move {
        break; // error[E0267]: `break` inside of an `async` block
    }
}
}

const

语法
ConstBlockExpression :
   const BlockExpression

const 块是块表达式的一种变体,其主体在编译时而不是运行时评估。

Const 块允许您定义常量值,而无需定义新的 常量条目,因此它们有时也称为内联常量。它还支持类型推断,因此无需像 常量条目 那样指定类型。

自由 常量条目不同,Const 块具有引用作用域内的泛型参数的能力。它们被脱糖为作用域内具有泛型参数的常量条目(类似于关联常量,但没有与之关联的 trait 或类型)。例如,以下代码

#![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 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() };
}

带标签的块表达式

带标签的块表达式记录在 循环和其他可中断表达式 部分。

块表达式上的属性

内部属性 允许直接放在块表达式的左大括号之后,在以下情况下:

在块表达式上具有意义的属性是 cfglint 检查属性

例如,此函数在 unix 平台上返回 true,在其他平台上返回 false

#![allow(unused)]
fn main() {
fn is_unix_platform() -> bool {
    #[cfg(unix)] { true }
    #[cfg(not(unix))] { false }
}
}