← [ BACK_TO_NOTES ]

Rust 闭包

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

#concept #growing #dev #rust

[!info] related notes

Rust 闭包

这个概念为什么出现

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

[!abstract] 一句话理解 闭包 |args| expr 可捕获环境;由闭包体如何使用捕获值决定实现哪些 Fn/FnMut/FnOnce,决定能调用几次以及能否修改/搬走环境。

最小可运行示例

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

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

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

fn main() {
    let discount_pct = 85u32;

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

建议运行:cargo run

期望输出:

[850, 1700, 425]

结合场景再看三个关注点

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

核心概念与准确模型

Trait能力
FnOnce调用按值接收闭包,可移出捕获值;所有闭包都实现
FnMut调用借用 &mut self,可修改捕获状态但不移出非 Copy 捕获值
Fn调用借用 &self,不需独占修改或移出捕获状态

语法:

let add = |a, b| a + b;
assert_eq!(add(1_i32, 2_i32), 3); // 调用提供推断信息
let add = |a: i32, b: i32| -> i32 { a + b };

Fn: FnMut: FnOnce 是能力包含关系。只知道 F: FnOnce 的调用者最多能消耗调用一次,不代表所有实现 FnOnce 的闭包都只能调用一次。

move 强制按值捕获(非 Copy 值移动、Copy 值复制),不决定调用 trait;move 捕获 String 后仅读取仍可实现 Fn。移动一个短生命周期引用不会延长其借用,也不会自动满足 static。

设计动机

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

边界与误区

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

[!warning] 常见误区:在 thread::spawn 里不 move 导致借用寿命不够 普通 spawn 要求闭包及返回值为 Send + static;move 仅转移捕获,不能修复其中短借用或 Rc。需要借用调用栈数据时可考虑 thread::scope。

工程实践

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

本节总结

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

自测题

  1. FnOnce 何时出现?
  2. move 关键字作用?
参考答案
  1. 所有闭包都实现 FnOnce;若调用时移出非 Copy 捕获值,则通常只实现 FnOnce,不能反复调用。
  2. 强制按值捕获环境,常用于线程。

延伸阅读与资料来源

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

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本