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]
结合场景再看三个关注点
- 闭包自动捕获
discount_pct - 迭代器 API 依赖闭包
- 线程场景常要
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。
工程实践
- 短逻辑闭包,长逻辑命名函数
- 明确
move意图 - 作为参数时写
impl Fn...或泛型 bound - 测试可注入闭包做依赖替换
本节总结
- 闭包 = 函数 + 环境
- Fn 家族描述调用能力
- 迭代器/并发大量依赖
自测题
FnOnce何时出现?move关键字作用?
参考答案
- 闭包会吃掉捕获值时。
- 强制按值捕获环境,常用于线程。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| The Book — Closures | 官方书 | 闭包 |
| std::ops::Fn | 标准库 | Fn 家族 |