块表达式
Syntax
BlockExpression →
{
InnerAttribute*
Statements?
}
BlockExpressionNoInnerAttributes →
{
Statements?
}
Statements →
Statement+
| Statement+ ExpressionWithoutBlock
| ExpressionWithoutBlock
块表达式或块是控制流表达式,也是项和变量声明的匿名命名空间作用域。
作为控制流表达式,块按顺序执行其组成的非项声明语句,然后执行其最后的可选表达式。
作为匿名命名空间作用域,项声明仅在块本身内有效,由 let 语句声明的变量从下一个语句到块结束都有效。有关更多详细信息,请参阅作用域章节。
块的语法是 {,然后是任何内部属性,然后是任意数量的语句,然后是可选的表达式(称为最终操作数),最后是 }。
语句通常需要后跟分号,但有两个例外:
- 项声明语句不需要后跟分号。
- 表达式语句通常需要后跟分号,除非其外部表达式是流控制表达式。
此外,语句之间允许使用额外的分号,但这些分号不影响语义。
求值块表达式时,除项声明语句外,每个语句都按顺序执行。
然后执行最终操作数(如果给出)。
当块包含最终操作数时,块具有该最终操作数的类型和值。
#![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
AsyncBlockExpression → async 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 的结果传播。
最后,break 和 continue 关键字不能用于从 async 块分支出去。因此以下代码是非法的:
#![allow(unused)]
fn main() {
loop {
async move {
break; // error[E0267]: `break` inside of an `async` block
}
}
}
const 块
Syntax
ConstBlockExpression → const 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
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() };
}
标记块表达式
标记块表达式在循环和其他可中断表达式部分中记录。
块表达式上的属性
在以下情况下,块表达式的开花括号之后直接允许使用内部属性:
- 函数和方法主体。
- 循环主体(
loop、while和for)。 - 用作语句的块表达式。
- 作为数组表达式、元组表达式、调用表达式和元组式结构体表达式元素的块表达式。
- 作为另一个块表达式的尾部表达式的块表达式。
在块表达式上有意义的属性是 cfg 和 lint 检查属性。
例如,此函数在 unix 平台上返回 true,在其他平台上返回 false。
#![allow(unused)]
fn main() {
fn is_unix_platform() -> bool {
#[cfg(unix)] { true }
#[cfg(not(unix))] { false }
}
}