强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Rust 系统编程语言完全教程 / 第13章:闭包

第13章:闭包

13.1 闭包基础

什么是闭包

闭包是可以捕获其环境中变量的匿名函数。

fn main() {
    let name = String::from("Rust");

    // 闭包:用 |参数| 表达式 的语法
    let greet = || println!("Hello, {}!", name);
    greet(); // Hello, Rust!

    // 带参数的闭包
    let add = |a, b| a + b;
    println!("3 + 5 = {}", add(3, 5));

    // 带类型标注的闭包
    let multiply = |a: i32, b: i32| -> i32 { a * b };
    println!("3 * 5 = {}", multiply(3, 5));

    // 多行闭包
    let complex = |x: i32| {
        let doubled = x * 2;
        let tripled = x * 3;
        doubled + tripled
    };
    println!("complex(10) = {}", complex(10));
}

闭包 vs 函数

特性函数 fn闭包
语法fn name(a: i32) -> i32|a| a + 1
捕获环境❌ 不能✅ 可以
类型标注必须可省略(可推断)
类型具名类型匿名类型(每个闭包独特类型)
fn add_one_fn(x: i32) -> i32 {
    x + 1
}

fn main() {
    let add_one_closure = |x: i32| x + 1;

    // 两者使用方式相同
    println!("函数: {}", add_one_fn(5));
    println!("闭包: {}", add_one_closure(5));

    // 闭包可以捕获环境变量
    let offset = 10;
    let add_offset = |x| x + offset;
    println!("带捕获: {}", add_offset(5));
}

13.2 三种闭包 Trait

Fn / FnMut / FnOnce

Trait捕获方式调用次数说明
FnOnce取得所有权仅一次消费捕获的变量
FnMut可变借用多次可修改捕获的变量
Fn不可变借用多次只读捕获的变量

层次关系: FnFnMutFnOnce

fn main() {
    // Fn:不可变借用
    let name = String::from("Alice");
    let greet = || println!("Hello, {}", name);
    greet();
    greet(); // ✅ 可以多次调用
    println!("name 仍然有效: {}", name);

    // FnMut:可变借用
    let mut count = 0;
    let mut increment = || {
        count += 1;
        println!("count = {}", count);
    };
    increment();
    increment();
    // 闭包不再使用后,count 可以访问
    drop(increment);
    println!("最终 count = {}", count);

    // FnOnce:取得所有权
    let name = String::from("Bob");
    let consume = move || {
        println!("消费: {}", name);
        drop(name);
    };
    consume();
    // consume(); // ❌ 不能再调用
}

编译器如何推断闭包类型

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

    // 只读访问 → Fn
    let fn_closure = || println!("{}", s);

    // 修改变量 → FnMut
    let mut v = vec![1, 2, 3];
    let mut fn_mut_closure = || v.push(4);

    // 移动所有权 → FnOnce
    let s2 = String::from("world");
    let fn_once_closure = move || println!("{}", s2);

    fn_closure();
    fn_mut_closure();
    fn_once_closure();
}

13.3 闭包作为参数

使用泛型约束

// 接受 Fn 闭包
fn apply_fn<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
    f(x)
}

// 接受 FnMut 闭包
fn apply_fn_mut<F: FnMut()>(mut f: F, times: usize) {
    for _ in 0..times {
        f();
    }
}

// 接受 FnOnce 闭包
fn apply_fn_once<F: FnOnce() -> String>(f: F) -> String {
    f()
}

fn main() {
    // Fn
    let double = |x| x * 2;
    println!("double(5) = {}", apply_fn(double, 5));

    // FnMut
    let mut log = Vec::new();
    apply_fn_mut(|| log.push("entry"), 3);
    println!("日志: {:?}", log);

    // FnOnce
    let name = String::from("Alice");
    let greeting = move || format!("Hello, {}!", name);
    println!("{}", apply_fn_once(greeting));
}

impl Trait 语法

fn apply(f: impl Fn(i32) -> i32, x: i32) -> i32 {
    f(x)
}

fn main() {
    let result = apply(|x| x * x, 5);
    println!("5² = {}", result);
}

13.4 闭包作为返回值

// 返回闭包需要使用 impl Fn 或 Box<dyn Fn>
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
    move |x| x + n
}

fn make_multiplier(n: i32) -> Box<dyn Fn(i32) -> i32> {
    Box::new(move |x| x * n)
}

fn main() {
    let add_5 = make_adder(5);
    let add_10 = make_adder(10);
    println!("add_5(3) = {}", add_5(3));
    println!("add_10(3) = {}", add_10(3));

    let mul_3 = make_multiplier(3);
    println!("mul_3(7) = {}", mul_3(7));
}

13.5 闭包捕获模式

fn main() {
    // Copy 类型:默认复制
    let x = 42;
    let closure = || println!("x = {}", x);
    closure();
    println!("x 仍然可用: {}", x);

    // 非 Copy 类型:默认借用
    let s = String::from("hello");
    let closure = || println!("s = {}", s);
    closure();
    println!("s 仍然可用: {}", s);

    // move 关键字:强制取得所有权
    let s2 = String::from("world");
    let closure = move || println!("s2 = {}", s2);
    closure();
    // println!("{}", s2); // ❌ s2 已移动到闭包中

    // move + Copy 类型:复制
    let y = 100;
    let closure = move || println!("y = {}", y);
    closure();
    println!("y 仍然可用: {}", y); // ✅ i32 是 Copy
}

13.6 函数式编程模式

链式调用

fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // 链式处理:过滤 → 转换 → 收集
    let result: Vec<i32> = numbers
        .iter()
        .filter(|&&x| x % 2 == 0)   // 保留偶数
        .map(|&x| x * x)             // 平方
        .collect();                    // 收集结果

    println!("偶数平方: {:?}", result); // [4, 16, 36, 64, 100]

    // fold(归约)
    let sum: i32 = (1..=100).fold(0, |acc, x| acc + x);
    println!("1到100的和: {}", sum);

    // scan(带状态的 map)
    let cumulative: Vec<i32> = vec![1, 2, 3, 4, 5]
        .iter()
        .scan(0, |state, &x| {
            *state += x;
            Some(*state)
        })
        .collect();
    println!("累积和: {:?}", cumulative); // [1, 3, 6, 10, 15]
}

高阶函数

fn apply_twice<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
    f(f(x))
}

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 curry<F>(f: F, a: i32) -> impl Fn(i32) -> i32
where
    F: Fn(i32, i32) -> i32,
{
    move |b| f(a, b)
}

fn main() {
    // apply_twice
    let add_3 = |x| x + 3;
    println!("apply_twice(add_3, 10) = {}", apply_twice(add_3, 10)); // 16

    // compose
    let double = |x| x * 2;
    let add_one = |x| x + 1;
    let double_then_add = compose(add_one, double);
    println!("compose(add_one, double)(5) = {}", double_then_add(5)); // 11

    // curry
    let add = |a, b| a + b;
    let add_10 = curry(add, 10);
    println!("curry(add, 10)(5) = {}", add_10(5)); // 15
}

函数指针

fn square(x: i32) -> i32 {
    x * x
}

fn apply(f: fn(i32) -> i32, x: i32) -> i32 {
    f(x)
}

fn main() {
    // 函数指针可以作为闭包使用
    let result = apply(square, 5);
    println!("square(5) = {}", result);

    // 也可以传入闭包(如果闭包不捕获环境)
    let result = apply(|x| x + 1, 5);
    println!("add_one(5) = {}", result);

    // 函数指针实现了所有三个 trait
    let f: fn(i32) -> i32 = square;
    let v: Vec<i32> = (1..=5).map(f).collect();
    println!("{:?}", v); // [1, 4, 9, 16, 25]
}

13.7 业务场景示例

事件处理系统

type EventCallback = Box<dyn FnMut(&str)>;

struct EventEmitter {
    listeners: std::collections::HashMap<String, Vec<EventCallback>>,
}

impl EventEmitter {
    fn new() -> Self {
        Self {
            listeners: std::collections::HashMap::new(),
        }
    }

    fn on(&mut self, event: &str, callback: impl FnMut(&str) + 'static) {
        self.listeners
            .entry(event.to_string())
            .or_insert_with(Vec::new)
            .push(Box::new(callback));
    }

    fn emit(&mut self, event: &str, data: &str) {
        if let Some(callbacks) = self.listeners.get_mut(event) {
            for callback in callbacks.iter_mut() {
                callback(data);
            }
        }
    }
}

fn main() {
    let mut emitter = EventEmitter::new();

    // 注册事件处理器
    emitter.on("click", |data| {
        println!("点击事件: {}", data);
    });

    let mut click_count = 0;
    emitter.on("click", move |_data| {
        click_count += 1;
        println!("点击计数: {}", click_count);
    });

    emitter.on("hover", |data| {
        println!("悬停事件: {}", data);
    });

    // 触发事件
    emitter.emit("click", "按钮A");
    emitter.emit("click", "按钮B");
    emitter.emit("hover", "图片C");
}

中间件管道

type Middleware = Box<dyn Fn(&str) -> String>;

struct Pipeline {
    middlewares: Vec<Middleware>,
}

impl Pipeline {
    fn new() -> Self {
        Self { middlewares: Vec::new() }
    }

    fn add(&mut self, middleware: impl Fn(&str) -> String + 'static) {
        self.middlewares.push(Box::new(middleware));
    }

    fn execute(&self, input: &str) -> String {
        let mut result = input.to_string();
        for middleware in &self.middlewares {
            result = middleware(&result);
        }
        result
    }
}

fn main() {
    let mut pipeline = Pipeline::new();

    pipeline.add(|s| s.trim().to_string());
    pipeline.add(|s| s.to_lowercase());
    pipeline.add(|s| s.replace(" ", "_"));
    pipeline.add(|s| format!("[{}]", s));

    let result = pipeline.execute("  Hello World  ");
    println!("结果: {}", result); // [hello_world]
}

13.8 本章小结

要点说明
闭包语法|参数| 表达式|参数| { 语句块 }
Fn不可变借用环境,可多次调用
FnMut可变借用环境,可多次调用
FnOnce取得所有权,仅可调用一次
move强制闭包取得所有权
函数指针fn(T) -> T 类型,实现了所有闭包 trait

扩展阅读

  1. Rust Book - 闭包 — 官方教程
  2. Rust Reference - Closures — 语言参考