定义
- 闭包(Closure)是可以保存进变量或者作为参数传递给其他函数的匿名函数
- 闭包和函数不同的是,闭包允许捕获调用者作用域中的值
基础使用
不带参数,不带返回值
fn main() {
// 定义闭包
let empty_closure = || {
println!("this is a empty closure")
};
// 执行闭包
empty_closure()
}
带参数,带返回值
fn main() {
// 输入一个数加1后的结果
let add_one_v1 = |x: u32| -> u32 {
x + 1
};
let i = add_one_v1(2);
println!("{}", i) // 3
}
简写:入参和出参编译器可以自动推导
fn main() {
// 返回值类型可以省略
let add_one_v2 = |x: u32| { x + 1 };
// 入参类型可以省略
let add_one_v3 = |x| { x + 1 };
// 表达式括号也可以省略
let add_one_v4 = |x| x + 1;
println!("{}",add_one_v2(2));
println!("{}",add_one_v3(2));
println!("{}",add_one_v4(2));
}
编译器不能推导两次
fn main() {
let return_x = |x| x;
println!("{}", return_x("hello")); // x 已经被推导为 &str 类型了
println!("{}", return_x(123)); // 不能再传入整型
}
带Fn trait的闭包
类似一个回调函数
// 实现一个缓存,只处理第一次传入的值并保存
struct Cache<T> {
callback: T,
value: Option<i32>,
}
impl<T> Cache<T>
where
T: Fn(i32) -> i32, // 泛型约束为Fn trait
{
// 传入一个闭包
fn creat_new(callback: T) -> Cache<T> {
Cache {
callback, // 结构体同名属性,可简写
value: None,
}
}
fn get_value(&mut self, value: i32) -> i32 {
match self.value {
None => {
let v = (self.callback)(value); // 获取闭包并调用
self.value = Some(v); // self 必须是可变的这里才能赋值
v
}
Some(val) => val
}
}
}
fn main() {
let mut cache= Cache::creat_new(|x| x + 1);
let v1 = cache.get_value(1);
println!("{}", v1); // 2
let v2 = cache.get_value(1);
println!("{}", v2); // 2
}
捕获环境中的变量
闭包不仅可以作为一个函数执行,还可以访问和使用闭包定义时所在作用域中的变量。根据闭包捕获变量的方式不同,闭包的行为和类型会有所不同
闭包捕获变量的方式也对应函数三种获取参数的方式
闭包有三种捕获环境变量的方式,被编码为如下三个Fn trait
- 通过不可变借用
Fn
:当一个闭包只需要读取环境中的变量时使用 - 通过可变借用
FnMut
:当一个闭包需要修改环境中的变量时使用 - 通过转移所有权
FnOnce
:当一个闭包需要获取环境中变量的所有权时使用,且这种捕获方式要求闭包只能被调用一次,因为捕获的变量所有权被转移后不能再被使用
不可变借用
fn main() {
let i = 5;
let add_i = |x| x + i;
println!("{}", add_i(5)) // 10
}
可变借用,环境变量和闭包都要是mut
fn main() {
let mut i = 5;
let mut modify_i = |x| i = x;
modify_i(10);
println!("{}", i) // 10
}
move
关键字转移所有权
fn main() {
let x = vec![1, 2, 3];
let equal_val = move |z| { z == x };
let y = vec![1, 2, 3];
assert!(equal_val(y));
// 再次使用时会报错
println!("{:?}",x);
}