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 | 不可变借用 | 多次 | 只读捕获的变量 |
层次关系: Fn ⊂ FnMut ⊂ FnOnce
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 |
扩展阅读
- Rust Book - 闭包 — 官方教程
- Rust Reference - Closures — 语言参考