Skip to content

闭包实战总结

> 综合运用闭包知识,通过实战示例巩固闭包定义、捕获和使用的核心技能。

完整示例

示例 1:任务调度器

rust
/// 任务调度器
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();
}
▶ Run

示例 2:函数组合

rust
// 函数组合:(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
}
▶ Run

示例 3:数据处理管道

rust
/// 数据处理管道
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
}
▶ Run

示例 4:事件系统

rust
use 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");
}
▶ Run

常见错误

错误 1:类型推断后的类型冲突

rust
fn 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;
}
▶ Run

错误 2:返回局部变量的引用

rust
fn main() {
    let s = String::from("hello");

    // ❌ 错误:返回的引用可能超出生命周期
    // let get_ref = || &s;

    // ✅ 正确:使用 move 确保生命周期
    let get_ref = move || &s;
    println!("{}", get_ref());
}
▶ Run

错误 3:借用冲突

rust
fn 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);
}
▶ Run

错误 4:闭包中的 move 问题

rust
fn main() {
    let x = 5;
    let y = 10;

    // move 会移动所有捕获的变量
    let closure = move || {
        println!("x = {}, y = {}", x, y);
    };

    // x 和 y 都已移动
    // println!("x = {}", x);  // ❌ 错误

    closure();
}
▶ Run

练习

练习 1:字符串转换

编写闭包,将字符串转换为大写:

rust
let to_upper = |s: String| s.to_uppercase();
▶ Run

练习 2:计数器

使用闭包实现一个简单的计数器:

rust
fn create_counter() -> impl Fn() -> i32 {
    // 实现
}
▶ Run

练习 3:链式处理

使用迭代器和闭包,处理以下数据:

rust
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 1. 过滤偶数
// 2. 平方
// 3. 取前 3 个
// 4. 求和
▶ Run

小结

闭包核心概念

概念说明
语法`
类型推断编译器根据使用推断
捕获方式不可变借用、可变借用、move
Fn Trait不可变借用,多次调用
FnMut Trait可变借用,多次调用
FnOnce Trait获取所有权,一次调用

Trait 继承关系

FnOnce (基类)

FnMut

Fn

关键要点

  1. 闭包是匿名函数,可以捕获环境变量
  2. 类型推断让闭包更简洁,但类型一旦确定就不能改变
  3. move 关键字用于转移所有权,常用于线程和长生命周期
  4. Fn/FnMut/FnOnce 定义了闭包的调用方式
  5. 闭包与迭代器结合使用,可以编写简洁的数据处理代码

小结

本章我们学习了:

  • 闭包的基本语法和类型推断
  • 三种捕获方式和 move 关键字
  • Fn、FnMut、FnOnce Trait 的区别
  • 闭包作为参数和返回值
  • 实战案例:任务调度器和数据处理管道

练习题

详见:练习题