Rust中闭包(closures)可以用来捕获其所在环境的匿名函数。
介绍
闭包:
- 是匿名函数
- 可保存为变量、作为参数
- 可在一个地方创建闭包,在另一个上下文中调用闭包来完成运算
- 可从其定义的作用域捕获值
定义
let closure1 = | 变量1[, 变量2: 类型, ...] | -> 返回值类型 { 函数体 }
// call closure
closure1(参数1, ...);
说明:
- 闭包不要求标注参数和返回值,由于闭包通常很小,编译器通常能推断出类型
- 闭包的定义最终只会为参数/返回值推断出唯一的数据类型
示例
fn add_v1 (x: i32) -> i32 { x + 1} // 函数
let add_v2 = |x: i32| -> i32 { x + 1}; // 带类型标注和返回值
let add_v2 = |x| { x + 1};
let add_v2 = |x| x + 1; // 函数体简单可以省略 {}
延迟计算
使用 struct 持有闭包及其调用结果,其中调用结果只有在需要时才执行对应的闭包,该模式叫延迟计算(lazy evaluation)
或记忆化(memoization)
适用场景:多次重复的耗时计算
struct 持有闭包:需指明 struct 闭包字段的类型
fn trait
- fn trait 由标准库提供
- 所有的闭包至少实现以下一个 trait:
示例
缓冲器(Cacher)示例:
struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
}
}
}
}
// 声明
let mut x = Cacher::new(|num| {
num
});
// 调用
x.vaue(1);
说明:
- 适用 HashMap 存储闭包执行结果
- 实例中 u32 的参数类型和返回值,可以通过引入多个泛型替代
使用闭包捕获环境
闭包可以访问它的作用域内的变量,但普通函数不能。
fn main() {
let x = 4;
let eq_x = | z | z == x;
// 函数不能获取作用域内的变量
// fn eq_x(z: i32) -> bool {
// z == x
// }
}
说明:
- 闭包从所在环境捕获值的方式:
- 取得所有权:FnOnce
- 可变借用:FnMut
- 不可变借用:Fn
- 创建闭包时,通过闭包对环境值的使用,Rust可以推断出使用了哪个 trait:
- 所有闭包都实现了 FnOnce
- 没有移动捕获变量的实现了 FnMut
- 无需可变访问捕获变量的闭包实现了 Fn
其他
- 在参数列表前使用 move 关键字,可以强制闭包取得它所使用的环境值的所有权
- 当将闭包传递给新线程以移动数据使其归新线程所有时,此技术最有用
let x = 1;
let eq_x = move | z | z == x; // 此后,x 的所有权交给 z,在后边不能在使用 x