Rust 闭包(Closure)完全指南
📚 一、闭包基本概念
1. 什么是闭包?
闭包是可以捕获其环境的匿名函数。它们可以存储为变量、作为参数传递,并在其他地方被调用。
2. 与普通函数的区别
// 普通函数
fn add(x: i32, y: i32) -> i32 {
x + y
}
// 闭包
let add_closure = |x: i32, y: i32| -> i32 { x + y };
let add_closure_inferred = |x, y| x + y; // 类型推断
🎯 二、闭包语法
1. 基本语法
// 1. 完整语法
let closure1 = |x: i32, y: i32| -> i32 {
x + y
};
// 2. 类型推断
let closure2 = |x, y| x + y;
// 3. 无参数
let closure3 = || println!("Hello!");
// 4. 单个参数可省略括号
let closure4 = |x| x * 2;
// 5. 多行闭包
let closure5 = |x: i32| {
let y = x * 2;
y + 1
};
2. 调用闭包
fn main() {
// 定义闭包
let add = |x, y| x + y;
let greet = || println!("Hello, World!");
let square = |x: i32| -> i32 { x * x };
// 调用闭包
let result = add(5, 3); // 8
greet(); // Hello, World!
let squared = square(4); // 16
println!("add(5, 3) = {}", result);
println!("square(4) = {}", squared);
// 闭包可以重复调用
println!("add(10, 20) = {}", add(10, 20)); // 30
}
🔧 三、闭包的三种特质
Rust 闭包实现了以下三个 trait 之一(或全部):
FnOnce- 可以调用一次,获取所有权FnMut- 可以多次调用,可变借用Fn- 可以多次调用,不可变借用
fn closure_traits() {
// 1. Fn - 不可变借用(最常见)
let x = 10;
let print_x = || println!("x = {}", x); // 捕获 x 的不可变引用
print_x(); // 可以多次调用
print_x();
// 2. FnMut - 可变借用
let mut counter = 0;
let mut increment = || {
counter += 1;
println!("Counter: {}", counter);
};
increment(); // Counter: 1
increment(); // Counter: 2
// 3. FnOnce - 获取所有权
let s = String::from("hello");
let consume_string = || {
println!("Consuming: {}", s);
drop(s); // 消耗所有权
};
consume_string(); // 只能调用一次
// consume_string(); // ❌ 编译错误:s 已被移动
}
📦 四、闭包捕获环境
1. 捕获方式
fn capture_modes() {
let x = 5;
let y = 10;
let mut z = 15;
let w = String::from("hello");
// 1. 不可变借用捕获
let print_sum = || println!("x + y = {}", x + y);
print_sum();
println!("x is still available: {}", x); // ✅
// 2. 可变借用捕获
let mut increment_z = || {
z += 1;
println!("z = {}", z);
};
increment_z(); // z = 16
// println!("z: {}", z); // ❌ 编译错误:z 被可变借用
// 3. 所有权捕获
let consume_w = move || {
println!("w = {}", w);
// w 的所有权被移动到闭包内
};
consume_w();
// println!("w: {}", w); // ❌ 编译错误:w 已被移动
}
2. move 关键字
fn move_keyword() {
// 示例1: 在多线程中使用
let data = vec![1, 2, 3, 4, 5];
// 使用 move 强制获取所有权
let closure = move || {
println!("Data: {:?}", data);
};
closure();
// println!("{:?}", data); // ❌ 错误:data 已被移动
// 示例2: 闭包传递给线程
use std::thread;
let numbers = vec![1, 2, 3];
thread::spawn(move || {
println!("Numbers in thread: {:?}", numbers);
}).join().unwrap();
// 示例3: 捕获多个变量
let s1 = String::from("hello");
let s2 = String::from("world");
let s3 = String::from("rust");
let closure = move || {
println!("{} {} {}", s1, s2, s3);
};
closure();
}
🔄 五、闭包作为参数
1. 函数参数
// 接受闭包作为参数
fn apply<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32, // F 实现了 Fn(i32) -> i32
{
f(x)
}
// 更通用的写法
fn apply_generic<F, T, R>(f: F, value: T) -> R
where
F: Fn(T) -> R,
T: Clone, // 如果需要多次使用
{
f(value.clone())
}
fn main() {
let double = |x| x * 2;
let square = |x| x * x;
println!("apply(double, 5) = {}", apply(double, 5)); // 10
println!("apply(square, 5) = {}", apply(square, 5)); // 25
// 内联闭包
println!("apply(|x| x + 1, 5) = {}", apply(|x| x + 1, 5)); // 6
}
2. 返回闭包
// 返回闭包
fn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
move |x| x * factor
}
// 返回 Box<dyn Fn> 处理不同类型
fn make_operation(op: &str) -> Box<dyn Fn(i32, i32) -> i32> {
match op {
"add" => Box::new(|x, y| x + y),
"sub" => Box::new(|x, y| x - y),
"mul" => Box::new(|x, y| x * y),
_ => Box::new(|x, y| x / y),
}
}
fn main() {
let double = make_multiplier(2);
let triple = make_multiplier(3);
println!("double(5) = {}", double(5)); // 10
println!("triple(5) = {}", triple(5)); // 15
let add = make_operation("add");
let mul = make_operation("mul");
println!("add(5, 3) = {}", add(5, 3)); // 8
println!("mul(5, 3) = {}", mul(5, 3)); // 15
}
🎨 六、闭包与迭代器
1. 常见迭代器方法
fn closures_with_iterators() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 1. map - 转换
let doubled: Vec<_> = numbers.iter()
.map(|x| x * 2)
.collect();
// 2. filter - 过滤
let evens: Vec<_> = numbers.iter()
.filter(|&x| x % 2 == 0)
.copied()
.collect();
// 3. fold - 累积
let sum = numbers.iter()
.fold(0, |acc, &x| acc + x);
// 4. for_each - 遍历
numbers.iter()
.for_each(|x| println!("Number: {}", x));
// 5. 链式调用
let result: i32 = numbers.iter()
.filter(|&x| x % 2 == 0) // 偶数
.map(|&x| x * 3) // 乘以3
.filter(|&x| x > 10) // 大于10
.sum(); // 求和
println!("Result: {}", result);
}
2. 复杂闭包示例
struct Person {
name: String,
age: u8,
salary: f64,
}
fn complex_closure_example() {
let people = vec![
Person { name: "Alice".to_string(), age: 25, salary: 50000.0 },
Person { name: "Bob".to_string(), age: 30, salary: 60000.0 },
Person { name: "Charlie".to_string(), age: 35, salary: 70000.0 },
Person { name: "Diana".to_string(), age: 28, salary: 55000.0 },
];
// 复杂的数据处理管道
let total_bonus: f64 = people.iter()
.filter(|p| p.age >= 30) // 30岁以上
.map(|p| (p.salary * 0.1)) // 10%奖金
.filter(|&bonus| bonus > 5000.0) // 奖金超过5000
.fold(0.0, |acc, bonus| acc + bonus); // 总和
println!("Total bonus: ${:.2}", total_bonus);
// 使用闭包分组
use std::collections::HashMap;
let age_groups: HashMap<u8, Vec<&Person>> = people.iter()
.fold(HashMap::new(), |mut map, person| {
let age_group = (person.age / 10) * 10; // 按10岁分组
map.entry(age_group).or_insert_with(Vec::new).push(person);
map
});
for (age_group, group) in age_groups {
println!("Age group {}: {} people", age_group, group.len());
}
}
🏗️ 七、闭包在结构体中的使用
1. 存储闭包
struct Cache<T, R>
where
T: Fn() -> R + Clone,
R: Clone,
{
calculation: T,
value: Option<R>,
}
impl<T, R> Cache<T, R>
where
T: Fn() -> R + Clone,
R: Clone,
{
fn new(calculation: T) -> Self {
Cache {
calculation,
value: None,
}
}
fn value(&mut self) -> R {
match &self.value {
Some(v) => v.clone(),
None => {
let v = (self.calculation)();
self.value = Some(v.clone());
v
}
}
}
}
fn main() {
let mut cache = Cache::new(|| {
println!("Calculating expensive value...");
42
});
println!("First call: {}", cache.value()); // 会计算
println!("Second call: {}", cache.value()); // 从缓存获取
}
2. 回调系统
struct EventHandler<F>
where
F: Fn(&str),
{
callbacks: Vec<F>,
}
impl<F> EventHandler<F>
where
F: Fn(&str),
{
fn new() -> Self {
EventHandler { callbacks: Vec::new() }
}
fn register(&mut self, callback: F) {
self.callbacks.push(callback);
}
fn trigger(&self, event: &str) {
for callback in &self.callbacks {
callback(event);
}
}
}
fn main() {
let mut handler = EventHandler::new();
// 注册多个闭包作为回调
handler.register(|event| println!("Callback 1 received: {}", event));
handler.register(|event| println!("Callback 2 says: Event '{}' occurred", event));
// 触发事件
handler.trigger("click");
handler.trigger("hover");
}
⚡ 八、性能优化技巧
1. 避免闭包开销
use std::time::Instant;
fn performance_comparison() {
let data: Vec<i32> = (0..1_000_000).collect();
// 方法1: 内联闭包
let start = Instant::now();
let sum1: i32 = data.iter()
.map(|&x| x * 2)
.sum();
let duration1 = start.elapsed();
// 方法2: 预定义闭包
let double = |x: i32| x * 2;
let start = Instant::now();
let sum2: i32 = data.iter()
.map(|&x| double(x))
.sum();
let duration2 = start.elapsed();
// 方法3: 函数
fn double_fn(x: i32) -> i32 { x * 2 }
let start = Instant::now();
let sum3: i32 = data.iter()
.map(|&x| double_fn(x))
.sum();
let duration3 = start.elapsed();
println!("Inline closure: {} in {:?}", sum1, duration1);
println!("Predefined closure: {} in {:?}", sum2, duration2);
println!("Function: {} in {:?}", sum3, duration3);
}
2. 闭包大小优化
fn closure_size_optimization() {
use std::mem;
// 小闭包
let small_closure = |x: i32| x + 1;
println!("Small closure size: {} bytes", mem::size_of_val(&small_closure));
// 捕获环境的闭包
let data = vec![1, 2, 3];
let medium_closure = || {
println!("Data: {:?}", data);
};
println!("Medium closure size: {} bytes", mem::size_of_val(&medium_closure));
// 大闭包
let large_data: Vec<i32> = (0..1000).collect();
let large_closure = move || {
println!("Large data length: {}", large_data.len());
};
println!("Large closure size: {} bytes", mem::size_of_val(&large_closure));
}
🔧 九、实用工具函数
1. 闭包工厂
fn create_calculator(operation: char) -> impl Fn(f64, f64) -> f64 {
match operation {
'+' => |x, y| x + y,
'-' => |x, y| x - y,
'*' => |x, y| x * y,
'/' => |x, y| {
if y == 0.0 {
f64::NAN
} else {
x / y
}
},
'^' => |x, y| x.powf(y),
_ => |_, _| 0.0,
}
}
fn main() {
let add = create_calculator('+');
let mul = create_calculator('*');
let pow = create_calculator('^');
println!("5 + 3 = {}", add(5.0, 3.0)); // 8.0
println!("5 * 3 = {}", mul(5.0, 3.0)); // 15.0
println!("5 ^ 3 = {}", pow(5.0, 3.0)); // 125.0
}
2. 记忆化(Memoization)
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::{Arc, Mutex};
struct Memo<F, A, R>
where
F: Fn(A) -> R,
A: Eq + Hash + Clone,
R: Clone,
{
calculation: Arc<F>,
cache: Mutex<HashMap<A, R>>,
}
impl<F, A, R> Memo<F, A, R>
where
F: Fn(A) -> R,
A: Eq + Hash + Clone,
R: Clone,
{
fn new(calculation: F) -> Self {
Memo {
calculation: Arc::new(calculation),
cache: Mutex::new(HashMap::new()),
}
}
fn compute(&self, arg: A) -> R {
{
let cache = self.cache.lock().unwrap();
if let Some(result) = cache.get(&arg) {
return result.clone();
}
}
let result = (self.calculation)(arg.clone());
let mut cache = self.cache.lock().unwrap();
cache.insert(arg, result.clone());
result
}
}
fn main() {
let expensive_closure = |x: i32| -> i32 {
println!("Computing for {}...", x);
x * x
};
let memo = Memo::new(expensive_closure);
println!("First call: {}", memo.compute(5)); // 会计算
println!("Second call: {}", memo.compute(5)); // 从缓存获取
println!("Third call: {}", memo.compute(10)); // 会计算
println!("Fourth call: {}", memo.compute(10)); // 从缓存获取
}
🧪 十、测试闭包
1. 单元测试
#[cfg(test)]
mod tests {
#[test]
fn test_basic_closure() {
let add = |x, y| x + y;
assert_eq!(add(2, 3), 5);
assert_eq!(add(-1, 1), 0);
}
#[test]
fn test_closure_capture() {
let x = 10;
let get_x = || x;
assert_eq!(get_x(), 10);
}
#[test]
fn test_move_closure() {
let s = String::from("hello");
let closure = move || s.len();
assert_eq!(closure(), 5);
}
#[test]
fn test_closure_in_struct() {
struct Processor<F>
where
F: Fn(i32) -> i32,
{
process: F,
}
let processor = Processor { process: |x| x * 2 };
assert_eq!((processor.process)(5), 10);
}
}
2. 集成测试
fn process_numbers<F>(numbers: &[i32], processor: F) -> Vec<i32>
where
F: Fn(i32) -> i32,
{
numbers.iter().map(|&x| processor(x)).collect()
}
#[cfg(test)]
mod integration_tests {
use super::*;
#[test]
fn test_process_numbers() {
let numbers = vec![1, 2, 3, 4, 5];
// 测试平方
let squared = process_numbers(&numbers, |x| x * x);
assert_eq!(squared, vec![1, 4, 9, 16, 25]);
// 测试加一
let incremented = process_numbers(&numbers, |x| x + 1);
assert_eq!(incremented, vec![2, 3, 4, 5, 6]);
}
}
🎯 十一、最佳实践
1. 何时使用闭包
// ✅ 适合使用闭包的场景
fn good_use_cases() {
// 1. 回调函数
let callback = |result| println!("Result: {}", result);
// 2. 事件处理
let click_handler = |x, y| println!("Clicked at ({}, {})", x, y);
// 3. 配置行为
let logger = |level: &str, message: &str| {
println!("[{}] {}", level, message);
};
// 4. 简单的转换逻辑
let data = vec![1, 2, 3];
let processed: Vec<_> = data.iter()
.map(|x| x * 2)
.filter(|x| *x > 3)
.collect();
// 5. 线程任务
use std::thread;
thread::spawn(|| {
println!("Running in thread");
});
}
// ❌ 不适合使用闭包的场景
fn bad_use_cases() {
// 1. 复杂的业务逻辑 - 应该用函数
// ❌ 不好的做法:
let complex_logic = |x: i32| -> i32 {
if x < 0 {
-1
} else if x == 0 {
0
} else if x < 10 {
x * 2
} else {
x + 10
}
};
// ✅ 好的做法:
fn complex_logic_fn(x: i32) -> i32 {
if x < 0 {
-1
} else if x == 0 {
0
} else if x < 10 {
x * 2
} else {
x + 10
}
}
// 2. 需要递归的情况
// ❌ 闭包递归很复杂
// let factorial = |n: u32| -> u32 {
// if n <= 1 { 1 } else { n * factorial(n - 1) } // ❌ 不能直接递归
// };
// ✅ 用函数
fn factorial(n: u32) -> u32 {
if n <= 1 { 1 } else { n * factorial(n - 1) }
}
}
2. 性能建议
fn performance_recommendations() {
// 1. 避免在热路径中创建闭包
let data = vec![1, 2, 3, 4, 5];
// ❌ 不好的做法:在循环中创建闭包
for _ in 0..1000 {
let processor = |x| x * 2; // 每次循环都创建新闭包
// 使用 processor
}
// ✅ 好的做法:提前创建闭包
let processor = |x| x * 2;
for _ in 0..1000 {
// 使用 processor
}
// 2. 考虑闭包大小
let large_data = vec![0u8; 1024]; // 1KB
let small_closure = || large_data.len(); // 闭包会包含 large_data
println!("Closure size: {} bytes", std::mem::size_of_val(&small_closure));
// 3. 使用引用而非 move
let config = vec![1, 2, 3];
// ❌ 不必要的 move
let closure1 = move || config.len();
// ✅ 使用引用
let closure2 = || config.len(); // 自动捕获引用
}
📦 十二、完整示例:配置系统
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
type Callback<T> = Box<dyn Fn(&T) + Send + Sync>;
struct EventSystem<T> {
callbacks: Arc<Mutex<HashMap<String, Vec<Callback<T>>>>>,
}
impl<T> EventSystem<T>
where
T: Clone + Send + Sync + 'static,
{
fn new() -> Self {
EventSystem {
callbacks: Arc::new(Mutex::new(HashMap::new())),
}
}
fn subscribe<F>(&self, event: &str, callback: F)
where
F: Fn(&T) + Send + Sync + 'static,
{
let mut callbacks = self.callbacks.lock().unwrap();
callbacks
.entry(event.to_string())
.or_insert_with(Vec::new)
.push(Box::new(callback));
}
fn emit(&self, event: &str, data: T) {
if let Some(callbacks) = self.callbacks.lock().unwrap().get(event) {
for callback in callbacks {
callback(&data);
}
}
}
}
fn main() {
// 创建事件系统
let system = EventSystem::new();
// 订阅事件
system.subscribe("user.login", |user: &String| {
println!("User logged in: {}", user);
});
system.subscribe("user.logout", |user: &String| {
println!("User logged out: {}", user);
});
system.subscribe("user.login", |user: &String| {
println!("[AUDIT] Login event for: {}", user);
});
// 触发事件
system.emit("user.login", "alice".to_string());
system.emit("user.logout", "bob".to_string());
system.emit("user.login", "charlie".to_string());
// 输出:
// User logged in: alice
// [AUDIT] Login event for: alice
// User logged out: bob
// User logged in: charlie
// [AUDIT] Login event for: charlie
}
🔄 十三、闭包类型注解
1. 何时需要类型注解
fn when_to_annotate() {
// 情况1: 编译器无法推断类型
// let closure = |x| x; // ❌ 错误:无法推断类型
let closure = |x: i32| x; // ✅ 明确指定
// 情况2: 返回闭包
fn returns_closure() -> impl Fn(i32) -> i32 {
|x| x * 2
}
// 情况3: 存储为 trait 对象
let closures: Vec<Box<dyn Fn(i32) -> i32>> = vec![
Box::new(|x: i32| x + 1),
Box::new(|x: i32| x * 2),
];
// 情况4: 作为结构体字段
struct Processor<F>
where
F: Fn(i32) -> i32,
{
processor: F,
}
let p = Processor { processor: |x: i32| x + 1 };
}
2. 类型别名
type StringProcessor = Box<dyn Fn(&str) -> String>;
struct Pipeline {
processors: Vec<StringProcessor>,
}
impl Pipeline {
fn new() -> Self {
Pipeline { processors: Vec::new() }
}
fn add_processor<F>(&mut self, processor: F)
where
F: Fn(&str) -> String + 'static,
{
self.processors.push(Box::new(processor));
}
fn process(&self, input: &str) -> String {
let mut result = input.to_string();
for processor in &self.processors {
result = processor(&result);
}
result
}
}
fn main() {
let mut pipeline = Pipeline::new();
pipeline.add_processor(|s| s.to_uppercase());
pipeline.add_processor(|s| s.replace(" ", "_"));
pipeline.add_processor(|s| format!("[{}]", s));
let result = pipeline.process("hello world");
println!("Result: {}", result); // [HELLO_WORLD]
}
🎯 总结要点
- 闭包是匿名函数,可以捕获环境变量
- 三种 trait:
FnOnce、FnMut、Fn move关键字 强制获取所有权- 类型推断 通常不需要显式注解
- 性能良好 通常与函数调用开销相同
- 灵活使用 作为参数、返回值、存储在结构体中
- 注意生命周期 特别是当闭包跨越作用域时
闭包是 Rust 中强大而灵活的特性,使得函数式编程风格和回调模式在 Rust 中变得非常自然。正确使用闭包可以编写出更简洁、更表达性的代码。