块表达式

语法

BlockExpression :

   {

      内部属性*

      语句?

   }

语句 :

      语句+

   | 语句+ 无块表达式

   | 无块表达式

块表达式,或称,是一种控制流表达式,也是项目和变量声明的匿名命名空间作用域。作为控制流表达式,块按顺序执行其组件非项目声明语句,然后执行其最终的可选表达式。作为匿名命名空间作用域,项目声明仅在块本身内部有效,而由 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

异步块是块表达式的一种变体,它计算结果为一个 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 的结果传播。

最后,breakcontinue 关键字不能用于从异步块中跳转。因此,以下代码是非法的

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

带标签的代码块表达式

带标签的代码块表达式在循环和其他可中断表达式部分有详细说明。

代码块表达式上的属性

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

对代码块表达式有意义的属性是cfglint 检查属性

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

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