闭包表达式
语法
闭包表达式 :
move
?
(||
||
闭包参数组?|
)
(表达式 |->
无约束类型组 块表达式)闭包参数组 :
闭包参数 (,
闭包参数)*,
?
闭包表达式 ,也称为 lambda 表达式或 lambda ,定义出 闭包类型 并求值为该类型的值。
闭包表达式的语法 move
关键字可选,随后是管道符号 (|
) 包裹的逗号分隔的 模式 列表,称为闭包参数,每个参数后面可选地跟随 :
和类型,然后是可选的 ->
和类型,称为 返回类型 ,随后的表达式,称为 闭包体操作数 。每个模式后面的可选类型是该模式的类型注解。如果存在返回类型,则闭包体必须是 块 。
闭包表达式即表示函数,该函数将一系列参数映射到跟随参数的表达式。
和 let
绑定 相同,闭包参数是无法拒绝的 模式 ,其类型注解可选,如果未给出类型,将从上下文中推断。
每个闭包表达式的类型是唯一的、匿名的。
值得注意的是,闭包表达式 捕获其环境 ,而普通的 函数定义 不会。
如果没有 move
关键字,闭包表达式 推断它如何从其环境中捕获每个变量 ,优先通过共享引用来捕获,借用闭包体内用到的所有外部变量。
如果需要,编译器将推断应该取代的是可变引用或者从环境中移动还是复制值 (取决于它们的类型) 。
通过在闭包前面加上 move
关键字,可以强制闭包通过复制或移动值来捕获其环境。
这通常用于确保闭包的生命周期为 'static
。
闭包类型实现的 trait
闭包类型实现哪些 trait 取决于如何捕获变量和捕获的变量的类型。
请参考 调用 trait 和强转 章节了解闭包实现 Fn
、 FnMut
和 FnOnce
的情况和时机。
如果每个捕获的变量的类型也实现了 trait ,则闭包类型实现 Send
和 Sync
。
示例
在这个例子中,我们定义了一个函数 ten_times
,它接受一个高阶函数参数,然后调用该函数,并传入一个闭包表达式作为参数,接着传入一个从环境中移动值的闭包表达式。
#![allow(unused)] fn main() { fn ten_times<F>(f: F) where F: Fn(i32) { // 遍历 0..10,对每个值调用 f 函数 for index in 0..10 { f(index); } } // 调用 ten_times 函数,并传入一个匿名函数作为参数,该匿名函数打印出 "hello" 和输入值 ten_times(|j| println!("hello, {}", j)); // 带有类型注解的调用方式,和上一行等价 ten_times(|j: i32| -> () { println!("hello, {}", j) }); let word = "konnichiwa".to_owned(); // 调用 ten_times 函数,并传入一个匿名函数作为参数,该匿名函数打印出 word 和输入值 // 由于匿名函数要使用外部变量 word,所以前面加上 move 关键字 ten_times(move |j| println!("{}, {}", word, j)); }
在闭包参数上的属性
在闭包参数上的属性遵循与 普通函数参数 相同的规则和限制。