闭包实战总结
> 综合运用闭包知识,通过实战示例巩固闭包定义、捕获和使用的核心技能。
完整示例
示例 1:任务调度器
rust
▶ Run/// 任务调度器
struct TaskScheduler {
tasks: Vec<Box<dyn Fn() -> String>>,
}
impl TaskScheduler {
fn new() -> Self {
TaskScheduler {
tasks: Vec::new(),
}
}
fn add_task<F>(&mut self, task: F)
where
F: Fn() -> String + 'static,
{
self.tasks.push(Box::new(task));
}
fn run_all(&self) -> Vec<String> {
self.tasks.iter().map(|task| task()).collect()
}
fn run_and_print(&self) {
println!("执行 {} 个任务:", self.tasks.len());
for (i, task) in self.tasks.iter().enumerate() {
println!(" 任务 {}: {}", i + 1, task());
}
}
}
fn main() {
let mut scheduler = TaskScheduler::new();
// 添加不同类型的任务
scheduler.add_task(|| {
"任务 1: 清理临时文件".to_string()
});
scheduler.add_task(|| {
"任务 2: 备份数据库".to_string()
});
// 捕获环境变量
let config = String::from("production");
scheduler.add_task(move || {
format!("任务 3: 发送 {} 环境报告", config)
});
scheduler.run_and_print();
}示例 2:函数组合
rust
▶ Run// 函数组合:(f ∘ g)(x) = f(g(x))
fn compose<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(B) -> C + 'static,
G: Fn(A) -> B + 'static,
A: 'static,
{
move |x| f(g(x))
}
fn main() {
// 定义简单的转换函数
let double = |x: i32| x * 2;
let add_one = |x: i32| x + 1;
let square = |x: i32| x * x;
// 组合函数
let double_then_add = compose(add_one, double);
let add_then_square = compose(square, add_one);
println!("double_then_add(5) = {}", double_then_add(5)); // 11
println!("add_then_square(5) = {}", add_then_square(5)); // 36
// 链式组合
let complex = compose(square, compose(add_one, double));
println!("complex(5) = {}", complex(5)); // 121
}示例 3:数据处理管道
rust
▶ Run/// 数据处理管道
struct DataPipeline<T> {
steps: Vec<Box<dyn Fn(T) -> T>>,
}
impl<T> DataPipeline<T> {
fn new() -> Self {
DataPipeline { steps: Vec::new() }
}
fn add_step<F>(&mut self, step: F)
where
F: Fn(T) -> T + 'static,
{
self.steps.push(Box::new(step));
}
fn execute(&self, mut input: T) -> T {
for step in &self.steps {
input = step(input);
}
input
}
}
fn main() {
let mut pipeline: DataPipeline<i32> = DataPipeline::new();
// 添加处理步骤
pipeline.add_step(|x| {
println!(" 步骤 1: {} * 2", x);
x * 2
});
pipeline.add_step(|x| {
println!(" 步骤 2: {} + 10", x);
x + 10
});
pipeline.add_step(|x| {
println!(" 步骤 3: {} / 2", x);
x / 2
});
// 执行管道
let result = pipeline.execute(5);
println!("结果:{}", result); // 15
}示例 4:事件系统
rust
▶ Runuse std::collections::HashMap;
/// 简单的事件系统
struct EventEmitter {
events: HashMap<String, Vec<Box<dyn Fn(&str)>>>,
}
impl EventEmitter {
fn new() -> Self {
EventEmitter {
events: HashMap::new(),
}
}
fn on<F>(&mut self, event: &str, callback: F)
where
F: Fn(&str) + 'static,
{
self.events
.entry(event.to_string())
.or_insert_with(Vec::new)
.push(Box::new(callback));
}
fn emit(&self, event: &str, data: &str) {
if let Some(callbacks) = self.events.get(event) {
println!("触发事件 '{}': {}", event, data);
for callback in callbacks {
callback(data);
}
}
}
}
fn main() {
let mut emitter = EventEmitter::new();
// 注册事件处理器
emitter.on("login", |user| {
println!(" [审计] 用户 {} 登录", user);
});
emitter.on("login", |user| {
println!(" [通知] 欢迎,{}!", user);
});
emitter.on("logout", |user| {
println!(" [审计] 用户 {} 登出", user);
});
// 触发事件
emitter.emit("login", "Alice");
emitter.emit("logout", "Alice");
}常见错误
错误 1:类型推断后的类型冲突
rust
▶ Runfn main() {
let add = |x, y| x + y;
let result1 = add(1, 2); // i32 类型被推断
// let result2 = add(1.0, 2.0); // ❌ 错误:类型冲突
// ✅ 正确:使用不同的闭包
let add_i32 = |x: i32, y: i32| x + y;
let add_f64 = |x: f64, y: f64| x + y;
}错误 2:返回局部变量的引用
rust
▶ Runfn main() {
let s = String::from("hello");
// ❌ 错误:返回的引用可能超出生命周期
// let get_ref = || &s;
// ✅ 正确:使用 move 确保生命周期
let get_ref = move || &s;
println!("{}", get_ref());
}错误 3:借用冲突
rust
▶ Runfn main() {
let mut data = vec![1, 2, 3];
// ❌ 错误:同时存在可变和不可变借用
// let mut closure = || {
// data.push(4); // 可变借用
// };
// println!("{:?}", data); // 不可变借用
// closure();
// ✅ 正确:先使用不可变借用,再使用可变借用
println!("{:?}", data);
{
let mut closure = || {
data.push(4);
};
closure();
}
println!("{:?}", data);
}错误 4:闭包中的 move 问题
rust
▶ Runfn main() {
let x = 5;
let y = 10;
// move 会移动所有捕获的变量
let closure = move || {
println!("x = {}, y = {}", x, y);
};
// x 和 y 都已移动
// println!("x = {}", x); // ❌ 错误
closure();
}练习
练习 1:字符串转换
编写闭包,将字符串转换为大写:
rust
▶ Runlet to_upper = |s: String| s.to_uppercase();练习 2:计数器
使用闭包实现一个简单的计数器:
rust
▶ Runfn create_counter() -> impl Fn() -> i32 {
// 实现
}练习 3:链式处理
使用迭代器和闭包,处理以下数据:
rust
▶ Runlet numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 1. 过滤偶数
// 2. 平方
// 3. 取前 3 个
// 4. 求和小结
闭包核心概念
| 概念 | 说明 |
|---|---|
| 语法 | ` |
| 类型推断 | 编译器根据使用推断 |
| 捕获方式 | 不可变借用、可变借用、move |
| Fn Trait | 不可变借用,多次调用 |
| FnMut Trait | 可变借用,多次调用 |
| FnOnce Trait | 获取所有权,一次调用 |
Trait 继承关系
FnOnce (基类)
↑
FnMut
↑
Fn关键要点
- 闭包是匿名函数,可以捕获环境变量
- 类型推断让闭包更简洁,但类型一旦确定就不能改变
- move 关键字用于转移所有权,常用于线程和长生命周期
- Fn/FnMut/FnOnce 定义了闭包的调用方式
- 闭包与迭代器结合使用,可以编写简洁的数据处理代码
小结
本章我们学习了:
- 闭包的基本语法和类型推断
- 三种捕获方式和 move 关键字
- Fn、FnMut、FnOnce Trait 的区别
- 闭包作为参数和返回值
- 实战案例:任务调度器和数据处理管道
练习题
详见:练习题