闭包
学习目标
- 理解闭包语法
- 掌握三种捕获模式
- 理解
Fn、FnMut、FnOnce 的区别
- 掌握闭包作为参数和返回值
核心概念
闭包语法
fn main() {
let add = |a, b| a + b;
println!("{}", add(2, 3));
let add = |a: i32, b: i32| -> i32 { a + b };
let complex = |x: i32| {
let doubled = x * 2;
let result = doubled + 1;
result
};
let offset = 10;
let add_offset = |x| x + offset;
println!("{}", add_offset(5));
}
捕获模式
fn main() {
let name = String::from("Rust");
let greet = || println!("Hello, {}!", name);
greet();
greet();
println!("name 仍可用: {}", name);
let mut count = 0;
let mut increment = || { count += 1; };
increment();
increment();
println!("count = {}", count);
let name = String::from("Rust");
let consume = move || {
println!("consumed: {}", name);
};
consume();
}
三种 Fn Trait
fn call_once<F: FnOnce() -> String>(f: F) -> String {
f()
}
fn call_mut<F: FnMut()>(mut f: F) {
f();
f();
}
fn call_fn<F: Fn()>(f: F) {
f();
f();
}
fn main() {
let name = String::from("Rust");
let consume = move || name;
call_once(consume);
let mut count = 0;
let mut inc = || { count += 1; };
call_mut(&mut inc);
println!("count = {}", count);
let greet = || println!("hello");
call_fn(greet);
}
闭包作为参数
fn apply<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(x)
}
fn main() {
let double = |x| x * 2;
let add_one = |x| x + 1;
println!("{}", apply(double, 5));
println!("{}", apply(add_one, 5));
}
闭包作为返回值
fn make_adder(offset: i32) -> impl Fn(i32) -> i32 {
move |x| x + offset
}
fn make_multiplier(factor: f64) -> Box<dyn Fn(f64) -> f64> {
Box::new(move |x| x * factor)
}
fn main() {
let add_5 = make_adder(5);
println!("{}", add_5(10));
let double = make_multiplier(2.0);
println!("{}", double(3.0));
}
闭包与迭代器
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
let parsed: Vec<i32> = vec!["1", "abc", "3"]
.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect();
numbers.iter().for_each(|x| print!("{} ", x));
}
实践练习
练习 1:函数组合
fn compose<F, G>(f: F, g: G) -> impl Fn(i32) -> i32
where
F: Fn(i32) -> i32,
G: Fn(i32) -> i32,
{
move |x| f(g(x))
}
fn main() {
let double = |x| x * 2;
let add_one = |x| x + 1;
let double_then_add = compose(add_one, double);
println!("{}", double_then_add(5));
}
练习 2:缓存
struct Cache<F>
where
F: Fn(i32) -> i32,
{
calculation: F,
value: Option<i32>,
}
impl<F> Cache<F>
where
F: Fn(i32) -> i32,
{
fn new(calculation: F) -> Self {
Cache { calculation, value: None }
}
fn get(&mut self, input: i32) -> i32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(input);
self.value = Some(v);
v
}
}
}
}
fn main() {
let mut cache = Cache::new(|x| {
println!("计算中...");
x * x
});
println!("结果: {}", cache.get(5));
println!("结果: {}", cache.get(5));
}
常见错误
1. 忘记 move
fn make_adder(offset: i32) -> impl Fn(i32) -> i32 {
move |x| x + offset
}
2. Fn vs FnMut 混淆
let mut count = 0;
let mut inc = || count += 1;
小结
| Trait | 调用次数 | 捕获方式 |
|---|
Fn | 多次 | 不可变借用 |
FnMut | 多次 | 可变借用 |
FnOnce | 一次 | 获取所有权 |
| 语法 | 说明 |
|---|
|x| x + 1 | 闭包 |
move || { } | 强制获取所有权 |
impl Fn(T) -> U | 闭包参数/返回值 |