Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

块表达式

Syntax
BlockExpression
    {
        InnerAttribute*
        Statements?
    }

BlockExpressionNoInnerAttributes
    {
        Statements?
    }

Statements
      Statement+
    | Statement+ ExpressionWithoutBlock
    | ExpressionWithoutBlock

块表达式是控制流表达式,也是项和变量声明的匿名命名空间作用域。

作为控制流表达式,块按顺序执行其组成的非项声明语句,然后执行其最后的可选表达式。

作为匿名命名空间作用域,项声明仅在块本身内有效,由 let 语句声明的变量从下一个语句到块结束都有效。有关更多详细信息,请参阅作用域章节。

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

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

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

此外,语句之间允许使用额外的分号,但这些分号不影响语义。

求值块表达式时,除项声明语句外,每个语句都按顺序执行。

然后执行最终操作数(如果给出)。

当块包含最终操作数时,块具有该最终操作数的类型和值。

#![allow(unused)]
fn main() {
let x: u8 = { 0u8 }; // `0u8` is the final operand.
assert_eq!(x, 0);
let x: u8 = { (); 0u8 }; // As above.
assert_eq!(x, 0);
}

当块不包含最终操作数且块不发散时,块具有单元类型单元值

#![allow(unused)]
fn main() {
let x: () = {}; // Has no final operand.
assert_eq!(x, ());
let x: () = { 0u8; }; // As above.
assert_eq!(x, ());
}

当块不包含最终操作数且块发散时,块具有永不类型且没有最终值(因为其类型是无人居住的)。

#![allow(unused)]
fn main() {
fn f() -> ! { loop {}; } // Diverges and has no final operand.
//          ^^^^^^^^^^^^
// The body of a function is a block expression.
}

Note

请注意,没有最终操作数的块与具有显式单元类型最终操作数的块是不同的。例如,即使此块发散,块的类型也是单元而不是永不

#![allow(unused)]
fn main() {
fn f() -> ! { loop {}; () } // ERROR: Mismatched types.
//          ^^^^^^^^^^^^^^^ This block has unit type.
}

Note

作为控制流表达式,如果块表达式是表达式语句的外部表达式,则预期类型为 (),除非它后面紧跟分号。

如果所有可达的控制流路径都包含发散表达式,则块被认为是发散的,除非该表达式是未被读取的位置表达式

#![allow(unused)]
fn main() {
#![ feature(never_type) ]
fn no_control_flow() -> ! {
    // There are no conditional statements, so this entire function body is diverging.
    loop {}
}

fn control_flow_diverging() -> ! {
    // All paths are diverging, so this entire function body is diverging.
    if true {
        loop {}
    } else {
        loop {}
    }
}

fn control_flow_not_diverging() -> () {
    // Some paths are not diverging, so this entire block is not diverging.
    if true {
        ()
    } else {
        loop {}
    }
}

// Note: This makes use of the unstable never type which is only available on
// Rust's nightly channel. This is done for illustration purposes. It is
// possible to encounter this scenario in stable Rust, but requires a more
// convoluted example.
struct Foo {
    x: !,
}

fn make<T>() -> T { loop {} }

fn diverging_place_read() -> ! {
    let foo = Foo { x: make() };
    // A read of a place expression produces a diverging block.
    let _x = foo.x;
}
}
#![allow(unused)]
fn main() {
#![ feature(never_type) ]
fn make<T>() -> T { loop {} }
struct Foo {
    x: !,
}
fn diverging_place_not_read() -> ! {
    let foo = Foo { x: make() };
    // Assignment to `_` means the place is not read.
    let _ = foo.x;
} // ERROR: Mismatched types.
}

块始终是值表达式,在值表达式上下文中求值最后一个操作数。

Note

如果确实需要,这可用于强制移动值。例如,以下示例在调用 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

Syntax
AsyncBlockExpressionasync move? BlockExpression

async 块是块表达式的变体,它求值为一个 future。

块的最终表达式(如果存在)确定 future 的结果值。

执行 async 块类似于执行闭包表达式:其直接效果是产生并返回一个匿名类型。

闭包返回实现一个或多个 std::ops::Fn trait 的类型,而 async 块返回的类型实现 std::future::Future trait。

此类型的实际数据格式未指定。

Note

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

2018 Edition differences

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

捕获模式

Async 块使用与闭包相同的捕获模式从其环境捕获变量。与闭包一样,当编写 async { .. } 时,每个变量的捕获模式将从块的内容推断。然而,async move { .. } 块将把所有引用的变量移入结果 future。

异步上下文

因为 async 块构造 future,它们定义了一个异步上下文,该上下文反过来可以包含 await 表达式。异步上下文由 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

Syntax
ConstBlockExpressionconst BlockExpression

const 块是块表达式的变体,其主体在编译时而不是运行时求值。

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

Syntax
UnsafeBlockExpressionunsafe 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 }
}
}