C13 Part1 闭包和迭代器
- [13.1 闭包(closures)](#13.1 闭包(closures))
-
- [13.1.1 函数式编程](#13.1.1 函数式编程)
- [13.1.2 闭包的定义](#13.1.2 闭包的定义)
- [13.2 闭包:类型推断和标注](#13.2 闭包:类型推断和标注)
-
- [13.2.1 闭包的类型推断](#13.2.1 闭包的类型推断)
- [13.2.2 函数和闭包的定义语法](#13.2.2 函数和闭包的定义语法)
- 注意
- [13.3 闭包:使用泛型和Fn Trait存储闭包](#13.3 闭包:使用泛型和Fn Trait存储闭包)
-
- [13.3.1 记忆化(延迟计算)模式](#13.3.1 记忆化(延迟计算)模式)
- [13.3.2 如何让struct持有闭包](#13.3.2 如何让struct持有闭包)
- [13.3.3 Fn Trait](#13.3.3 Fn Trait)
- [13.4 闭包:使用闭包捕获上下文](#13.4 闭包:使用闭包捕获上下文)
-
- [13.4.1 闭包可以捕获他们所在的环境](#13.4.1 闭包可以捕获他们所在的环境)
- [13.4.2 闭包从所在环境捕获值的方式](#13.4.2 闭包从所在环境捕获值的方式)
- [13.4.3 move关键字](#13.4.3 move关键字)
13.1 闭包(closures)
13.1.1 函数式编程
- 把函数作为参数
- 将函数作为其他参数的返回值
- 把函数赋给一个变量,被以后执行
13.1.2 闭包的定义
- 闭包:可以捕获其所在环境的匿名函数
- 特点
- 是匿名函数
- 保存为变量,作为参数
- 可以在一个地方创建闭包,然后在另一个上下文中调用闭包来完成运算
- 可从其定义的作用域捕获值
- 例子
- 仅在必要时调用该算法
- 只调用一次
已知下面的代码:
thread_cost_long_time会被调用多次,并且thread_cost_long_time会比较耗时,需要使用闭包来改进下面的代码
rust
fn thread_cost_long_time(time: u32) -> u32 {
println!("Cost long time ....");
thread::sleep(Duration::from_secs(2));
time
}
fn generate_workout(time: u32, random_number: u32)
{
if time < 25
{
println!("cost time {} seconds", thread_cost_long_time(time));
println!("Again, cost time {} seconds", thread_cost_long_time(time));
} else {
if random_number == 3 {
println!("Break down")
} else {
println!("Again, and again cost time {} seconds", thread_cost_long_time(time));
}
}
}
改进1:
rust
fn thread_cost_long_time(time: u32) -> u32 {
println!("Cost long time ....");
thread::sleep(Duration::from_secs(2));
time
}
fn generate_workout(time: u32, random_number: u32)
{
// 改成在if调用之前,这样就不会调用两次
// 但是 会导致每次都需要计算,(下面的代码逻辑是当random_number =3的时候是不需要long_cost 的值的)=>会导致浪费
let long_cost = thread_cost_long_time(time);
if time < 25
{
println!("cost time {} seconds", long_cost);
println!("Again, cost time {} seconds", long_cost);
} else {
if random_number == 3 {
println!("Break down")
} else {
println!("Again, and again cost time {} seconds", long_cost);
}
}
}
期望:函数定义在一个地方,真正需要结果时才执行相关的代码
- 闭包定义
定义一个匿名函数
// 保存为一个变量long_closure
||之间是函数参数,如果有两个参数的话使用,分隔
{}内部是闭包的函数体
注意它是一个赋值语句,最后需要添加;
rust
let long_closure = |time| {
println!("Cost long time ....");
thread::sleep(Duration::from_secs(2));
time
};
- 闭包的调用
long_closure(time)
rust
fn generate_workout(time: u32, random_number: u32)
{
let long_closure = |time| {
println!("Cost long time ....");
thread::sleep(Duration::from_secs(2));
time
};
if time < 25
{
println!("cost time {} seconds", long_closure(time));
println!("Again, cost time {} seconds", long_closure(time));
} else {
if random_number == 3 {
println!("Break down")
} else {
println!("Again, and again cost time {} seconds", long_closure(time));
}
}
}
13.2 闭包:类型推断和标注
13.2.1 闭包的类型推断
- 闭包不要求标注参数和返回值的类型
- 闭包通常很短小,编译器通常能推出类型
- 可以手动添加类型标注
rust
let long_closure = |time:u32| -> u32 {
println!("Cost long time ....");
thread::sleep(Duration::from_secs(2));
time
};
13.2.2 函数和闭包的定义语法
函数
fn add_one_v1 (x: u32) -> u32 { x+1 }
闭包
let add_one_v2 = | x: u32| -> u32 { x+1 };
let add_one_v3 = | x | { x+1 };
let add_one_v4 = | x | x+1 ;
注意
闭包的定义最终只会为参数/返回值推断出唯一具体的类型
例子:
rust
let example_closure = |x|x;
let s = example_closure(String::from("Hello"));
// 在这里会报错,它会要求example_closure传入的参数类型是String
let n = example_closure(10);
13.3 闭包:使用泛型和Fn Trait存储闭包
13.3.1 记忆化(延迟计算)模式
- 记忆化(延迟计算)模式:创建一个struct,它持有闭包及其调用结果
- 只会在需要结果时才执行该闭包
- 可缓存结果->结果不同时可以使用map
13.3.2 如何让struct持有闭包
- struct的定义需要知道所有字段的类型
- 需要指明闭包的类型
- 每个闭包实例都有自己的唯一的匿名类型,即使两个闭包签名完全一样
- 需要使用泛型和Trait Bound
13.3.3 Fn Trait
-
Fn Trait由标准库提供
-
所有的闭包都至少实现了以下trait之一:
- Fn
- FnMut
- FnOnce
-
例子
rust
fn generate_workout(time: u32, random_number: u32)
{
// Cacher::new 里面传入的是一个闭包,即caculation代表一个闭包
let mut long_closure = Cacher::new(|time| {
println!("Cost long time ....");
thread::sleep(Duration::from_secs(2));
time
});
if time < 25
{
println!("cost time {} seconds", long_closure.value(time));
println!("Again, cost time {} seconds", long_closure.value(time));
} else {
if random_number == 3 {
println!("Break down")
} else {
println!("Again, and again cost time {} seconds", long_closure.value(time));
}
}
}
struct Cacher<T>
where T : Fn(u32)->u32,
{
caculation: T,
value: Option<u32>
}
impl<T> Cacher<T>
where T : Fn(u32)->u32, {
fn new(caculation: T) -> Cacher<T> {
Cacher { caculation, value: None }
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
// 如果value是有值的,直接返回
Some(v) => v,
None => {
// 如果value是没有值的,则执行一下caculation并且将arg赋值给对应的value
// 注意:(self.caculation)(arg)是使用caculation闭包执行函数,我第一次没有看懂
let v = (self.caculation)(arg);
self.value = Some(v);
v
}
}
}
}
限制:
- Cacher实例假定针对不同的参数arg,value方法总会得到相同的值
-> 使用HashMap代替单个值:
key: arg参数
value:执行闭包的结果
修改为使用HashMap存储Value
rust
struct Cacher<T>
where T : Fn(u32)->u32,
{
caculation: T,
value: HashMap<u32, u32>
}
impl<T> Cacher<T>
where T : Fn(u32)->u32, {
fn new(caculation: T) -> Cacher<T> {
Cacher { caculation, value: HashMap::new() }
}
fn value(&mut self, arg: u32) -> u32 {
*self.value.entry(arg).or_insert((self.caculation)(arg))
}
}
#[test]
fn test_return_some_res()
{
let mut cacher = Cacher::new(|x| x+1);
let x1 = cacher.value(1);
assert_eq!(x1, 2);
let x2 = cacher.value(2);
assert_eq!(x2, 3);
}
- 只能接收一个u32类型的参数和u32类型的返回值
-> 引入多个泛型
13.4 闭包:使用闭包捕获上下文
13.4.1 闭包可以捕获他们所在的环境
- 闭包可以访问定义它的作用域内的变量,而普通函数则不行
- 会产生内存开销
- 例子
rust
let x = 4;
let equal_to_x = |z| z==x;
let y = 3;
println!("{}", equal_to_x(y));
println!("{}", x);
输出结果:
false
4
13.4.2 闭包从所在环境捕获值的方式
- 与函数获得参数的三种方式一样:
- 取得所有权:FnOnce
- 可变借用:FnMut
- 不可变借用:Fn
- 创建闭包时,通过闭包对环境值得使用,Rust推断出具体使用哪个trait:
- 所有得闭包都实现了FnOnce
- 没有移动捕获变量得实现了FnMut
- 无需可变访问捕获变量得闭包实现了Fn
13.4.3 move关键字
- 在参数列表前使用move关键字
- 强制闭包取得它所使用得环境值得所有权
- 当闭包传递给新线程以移动数据使其归新线程所有时,此技术最为有用
例子
rust
let x = vec![1,2,3];
let equal_to_x = move |z| x.contains(z);
let y = 3;
println!("{}", equal_to_x(&y));
println!("{:?}", x);

2026/1/16