Rust 闭包

闭包语法、环境捕获、Fn/FnMut/FnOnce 家族与在迭代器/线程中的使用边界。

#type / concept #status / growing #tech / dev #resource / rust

[!info] 关联笔记

Rust 闭包

这个概念为什么出现

要把一小段逻辑就地传给 map/thread::spawn,并捕获周围变量
闭包是带环境的匿名函数。

[!abstract] 一句话理解 闭包 |args| expr 可捕获环境;按捕获方式对应 Fn/FnMut/FnOnce,决定能调用几次以及能否修改/搬走环境。

最小可运行示例

场景:价格引擎——捕获折扣率的闭包批量映射

促销活动有统一折扣率;对价格列表用闭包映射折后价。

// 业务意图:用捕获的折扣率批量计算折后价。
// 教学点:闭包捕获;map;move 闭包预告。

fn main() {
    let discount_bps: u32 = 850; // 8.5 折 = 8500/10000? 这里用 85%
    let discount_pct = 85u32;

    let prices = [1000u32, 2000, 500];
    let discounted: Vec<u32> = prices
        .iter()
        .map(|p| p * discount_pct / 100)
        .collect();
    println!("{discounted:?}");
}

建议运行:cargo run

期望输出:

[850, 1700, 425]

结合场景再看三个关注点

  1. 闭包自动捕获 discount_pct
  2. 迭代器 API 依赖闭包
  3. 线程场景常要 move 闭包 转移所有权

核心概念与准确模型

Trait能力
FnOnce最多调用一次,可搬走捕获
FnMut可多次,可变借用捕获
Fn可多次,不可变借用捕获

语法:

let add = |a, b| a + b;
let add = |a: i32, b: i32| -> i32 { a + b };

move 强制按值捕获。

设计动机

  • 就地定制算法
  • 与所有权一致的捕获规则
  • 零成本:常内联单态化

边界与误区

  • 生命周期:闭包不能返回指向短暂捕获的引用
  • 类型通常不可写出,靠推断与 impl Fn
  • 递归闭包麻烦

[!warning] 常见误区:在 thread::spawn 里不 move 导致借用寿命不够 线程可能活过当前栈帧,需 move 或保证 'static

工程实践

  1. 短逻辑闭包,长逻辑命名函数
  2. 明确 move 意图
  3. 作为参数时写 impl Fn... 或泛型 bound
  4. 测试可注入闭包做依赖替换

本节总结

  • 闭包 = 函数 + 环境
  • Fn 家族描述调用能力
  • 迭代器/并发大量依赖

自测题

  1. FnOnce 何时出现?
  2. move 关键字作用?
参考答案
  1. 闭包会吃掉捕获值时。
  2. 强制按值捕获环境,常用于线程。

延伸阅读与资料来源

资料类型支撑内容
The Book — Closures官方书闭包
std::ops::Fn标准库Fn 家族
创建于 2026/7/15 更新于 2026/7/15