闭包表达式

语法
闭包表达式 :
   async1?
   move?
   ( || | | 闭包参数? | )
   (表达式 | -> 不带边界的类型 块表达式)

闭包参数 :
   闭包参数项 (, 闭包参数项)* ,?

闭包参数项 :
   外部属性* 不带顶层alt的模式 ( : 类型 )?

1

在 2015 版本中不允许使用 async 限定符。

一个 闭包表达式,也称为 lambda 表达式或 lambda,定义一个闭包类型并求值为该类型的值。闭包表达式的语法是一个可选的 async 关键字,一个可选的 move 关键字,然后是一个由管道符号 (|) 分隔的逗号分隔的模式列表,称为 闭包参数,每个可选地跟随 : 和一个类型,然后是一个可选的 -> 和类型,称为 返回类型,然后是一个表达式,称为 闭包体操作数

每个模式后面的可选类型是该模式的类型标注。

如果存在返回类型,闭包体必须是

闭包表达式表示一个函数,它将参数列表映射到参数后面的表达式。就像一个let 绑定一样,闭包参数是无可辩驳的模式,它们的类型标注是可选的,如果未给出,将从上下文中推断出来。

每个闭包表达式都有一个唯一、匿名的类型。

值得注意的是,闭包表达式会捕获其环境,这是常规函数定义所没有的。

如果没有 move 关键字,闭包表达式会推断如何捕获环境中的每个变量,优先通过共享引用捕获,实际上是借用闭包体内部提到的所有外部变量。

如果需要,编译器会推断出应该获取可变引用,或者应该从环境中移动或复制(取决于类型)值。

通过在闭包前加上 move 关键字,可以强制闭包通过复制或移动值来捕获其环境。这通常用于确保闭包的生命周期是 'static

闭包的特性实现

闭包类型实现了哪些特性取决于变量如何被捕获、被捕获变量的类型以及是否存在 async。关于闭包如何以及何时实现 FnFnMutFnOnce 特性,请参见调用特性和强制转换章节。如果每个被捕获变量的类型都实现了相应的特性,则闭包类型会实现 SendSync 特性。

异步闭包

async 关键字标记的闭包表示它们是异步的,这类似于异步函数

调用异步闭包并不会立即执行任何工作,而是求值得到一个实现 Future 特性的值,该值对应于闭包体的计算。

#![allow(unused)]
fn main() {
async fn takes_async_callback(f: impl AsyncFn(u64)) {
    f(0).await;
    f(1).await;
}

async fn example() {
    takes_async_callback(async |i| {
        core::future::ready(i).await;
        println!("done with {i}.");
    }).await;
}
}

版本差异:异步闭包仅在 Rust 2018 及更高版本中可用。

示例

在这个例子中,我们定义了一个函数 ten_times,它接受一个高阶函数参数,然后我们调用它,传入一个闭包表达式作为参数,接着是一个从其环境中移动值的闭包表达式。

#![allow(unused)]
fn main() {
fn ten_times<F>(f: F) where F: Fn(i32) {
    for index in 0..10 {
        f(index);
    }
}

ten_times(|j| println!("hello, {}", j));
// With type annotations
ten_times(|j: i32| -> () { println!("hello, {}", j) });

let word = "konnichiwa".to_owned();
ten_times(move |j| println!("{}, {}", word, j));
}

闭包参数上的属性

闭包参数上的属性遵循与常规函数参数相同的规则和限制。