讲解

闭包(closure)是可以捕获周围环境的匿名函数,写法是竖线包参数:|x| x + 1。类型标注大多可省(编译器从用法推断);捕获环境分三种强度:只读借用(默认,够用就好)、可变借用(闭包里改外部变量,要求闭包声明 mut)、拿走所有权(move 关键字,多线程场景必须,第 24 章见)。闭包实现了 Fn/FnMut/FnOnce 三个 trait 中的一种或多种,这决定了它能不能被多次调用、能不能改环境——细节先了解,用到时编译器会提示。

迭代器(iterator)是 Rust 处理序列的统一抽象,核心方法就一个:next() 返回 Option(None 表示结束)。但真正日常用的是两类组合方法:适配器(adapter)——map、filter、enumerate、take、rev,消费一个迭代器产出新迭代器,惰性执行、不耗性能;消费者(consumer)——collect、sum、count、for_each、fold,真正驱动迭代跑起来并产出结果。

「惰性」是关键提醒:v.iter().map(|x| x * 2) 本身什么都不做,没有消费者收尾这行代码等于没写(编译器会发警告)。性能上尽管放心:迭代器链经编译器优化后与手写循环等价(零成本抽象),所以放心写声明式的链式代码,不必为性能退回下标循环。

示例

闭包的三种捕获:

fn main() {
    // 只读借用环境
    let factor = 3;
    let multiply = |x: i32| x * factor;
    assert_eq!(multiply(4), 12);
    println!("factor 仍可用:{factor}");

    // 可变借用:闭包自己也要 mut
    let mut total = 0;
    let mut accumulate = |x: i32| {
        total += x;
        total
    };
    assert_eq!(accumulate(5), 5);
    assert_eq!(accumulate(5), 10);

    // move:拿走所有权(线程场景必需)
    let owned = String::from("我的数据");
    let show = move || println!("闭包拿到了:{owned}");
    show();
    // owned 已被移动,这里不能再用
}

迭代器链:适配器 + 消费者:

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

    // 偶数的平方求和:一条链读完
    let sum: i32 = nums
        .iter()
        .filter(|n| *n % 2 == 0)
        .map(|n| n * n)
        .sum();
    assert_eq!(sum, 220);

    // collect 把迭代器收成 Vec
    let labels: Vec<String> = nums
        .iter()
        .take(3)
        .enumerate()
        .map(|(i, n)| format!("第{}项:{n}", i + 1))
        .collect();
    assert_eq!(labels, vec!["第1项:1", "第2项:2", "第3项:3"]);

    // find / any / all 这类短路消费者也好用
    assert!(nums.iter().any(|n| *n > 9));
    assert!(nums.iter().all(|n| *n > 0));
    println!("迭代器链全部验证通过");
}

常见坑

  • map 之后没有消费者:v.iter().map(...) 单独一行不执行任何操作,编译器会警告 must be used;收尾用 collect/sum/for_each。
  • 闭包里修改环境忘了 mut:闭包变量本身要 let mut,且捕获期与外部的其他借用不能冲突——和普通借用规则一致。
  • move 之后还想用原值:move 闭包拿走所有权,原变量失效;只想借用就别写 move(线程场景例外,必须 move)。
  • 迭代中修改原集合:借用规则禁止;模式是「迭代旧的、collect 新的、替换」或者先收集索引再改。

小结

闭包 = 捕获环境的匿名函数,三种捕获强度;迭代器 = 惰性适配器 + 消费者,零成本抽象放心链。下一章看能突破编译期借用限制的三个智能指针。