闭包
学习目标
- 理解闭包语法
- 掌握三种捕获模式
- 理解
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)); // 15
}
捕获模式
fn main() {
// 不可变借用(&T)
let name = String::from("Rust");
let greet = || println!("Hello, {}!", name);
greet();
greet(); // 可以多次调用
println!("name 仍可用: {}", name);
// 可变借用(&mut T)
let mut count = 0;
let mut increment = || { count += 1; };
increment();
increment();
println!("count = {}", count);
// 移动所有权(T)
let name = String::from("Rust");
let consume = move || {
println!("consumed: {}", name);
};
consume();
// println!("{}", name); // ❌ 已移动
}
三种 Fn Trait
// FnOnce: 只能调用一次(消耗捕获的值)
fn call_once<F: FnOnce() -> String>(f: F) -> String {
f() // 只调用一次
}
// FnMut: 可以修改捕获的值
fn call_mut<F: FnMut()>(mut f: F) {
f();
f();
}
// Fn: 不修改,不消耗
fn call_fn<F: Fn()>(f: F) {
f();
f();
}
fn main() {
let name = String::from("Rust");
// FnOnce
let consume = move || name;
call_once(consume);
// FnMut
let mut count = 0;
let mut inc = || { count += 1; };
call_mut(&mut inc);
println!("count = {}", count);
// Fn
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)); // 10
println!("{}", apply(add_one, 5)); // 6
}
闭包作为返回值
fn make_adder(offset: i32) -> impl Fn(i32) -> i32 {
move |x| x + offset // 必须用 move 转移所有权
}
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)); // 15
let double = make_multiplier(2.0);
println!("{}", double(3.0)); // 6.0
}
闭包与迭代器
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// map
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
// filter
let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
// filter_map
let parsed: Vec<i32> = vec!["1", "abc", "3"]
.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect();
// for_each
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)); // 11
}
练习 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 {
// ❌ 编译错误:捕获了局部变量的引用
// |x| x + offset
// ✅
move |x| x + offset
}
2. Fn vs FnMut 混淆
let mut count = 0;
let mut inc = || count += 1;
// fn call_fn<F: Fn()>(f: F) { f(); }
// call_fn(inc); // ❌ inc 是 FnMut,不是 Fn
小结
| Trait | 调用次数 | 捕获方式 |
|---|---|---|
Fn | 多次 | 不可变借用 |
FnMut | 多次 | 可变借用 |
FnOnce | 一次 | 获取所有权 |
| 语法 | 说明 |
|---|---|
|x| x + 1 | 闭包 |
move || { } | 强制获取所有权 |
impl Fn(T) -> U | 闭包参数/返回值 |
练习编辑器
rust
Loading...