
匿名函数的艺术:不用取名,照样能干活
你有没有遇到过这种情况:想写个小函数,就为了用那么一次,结果还得正儿八经地给它取个名字、定义参数、写返回值……感觉就像为了拧个螺丝钉,专门跑去买了个电钻。
在 Rust 里,有个好东西叫闭包(Closure),让你能随手写个"一次性函数",不用取名,随写随用。今天咱们就聊聊这个让代码更简洁的神器。
闭包,说白了就是匿名函数。它不需要名字,可以直接写在需要它的地方。
生活化类比:
想象你去餐厅点餐:
// 最简单的闭包
let add = |x, y| x + y;
// 调用方式
let result = add(, ); // 8
看到了吗?|x, y| 这部分是参数列表,后面的 x + y 是函数体。就这么简单!
Rust 的闭包有三种"性格",取决于它怎么使用环境变量:
类型 | 语法 | 特点 |
|---|---|---|
Fn | ` | |
FnMut | ` | |
FnOnce | ` |
生活化类比:
fn main() {
let mut numbers = vec![, , , , ];
// 用闭包定义排序规则:从大到小
numbers.sort_by(|a, b| b.cmp(a));
println!("{:?}", numbers); // [9, 8, 5, 2, 1]
}
看到没?|a, b| b.cmp(a) 这个闭包直接写在 sort_by 里面,不用单独定义函数,多清爽!
fn main() {
let x = ;
// 闭包可以"捕获"外部的 x
let add_x = |n| n + x;
println!("{}", add_x()); // 50
}
这个闭包"记住"了外面的 x = 42,每次调用都加上这个值。
fn main() {
let mut count = ;
// 这个闭包要修改 count
let mut increment = || count += ;
increment(); // ✅ 可以
increment(); // ✅ 可以
increment(); // ✅ 可以
println!("count = {}", count); // ❌ 编译错误!
// 错误信息:cannot borrow `count` as immutable
// because it is also borrowed as mutable
}
编译器在说什么人话?
编译器说:"你那个闭包已经可变借用了 count,现在你又想不可变借用它来打印,不行!"
修复方法:
fn main() {
let mut count = ;
{
let mut increment = || count += ;
increment();
increment();
increment();
} // 闭包的作用域结束,借用释放
println!("count = {}", count); // ✅ 现在可以了
}
有时候你想让闭包"拥有"变量,而不是借用:
fn main() {
let name = String::from("Rust");
// 用 move 让闭包拥有 name
let greet = move || {
println!("Hello, {}!", name);
};
greet(); // Hello, Rust!
// greet(); // ❌ 第二次调用会失败,name 被移动了
}
生活化类比:
fn main() {
let add = |x, y| x + y;
let result1 = add(, ); // ✅ i32
let result2 = add(1.0, 2.0); // ❌ 编译错误!
}
编译器在说什么?
"你第一次调用用的是 i32,所以这个闭包的类型就固定为 i32 了,不能再用 f64 调用!"
解决方案:
显式指定类型:
let add = |x: f64, y: f64| x + y;
fn main() {
let numbers = vec![, , , , ];
// ❌ 这样写不行
fn process(nums: Vec<i32>, f: |i32| -> i32) {
// ...
}
}
正确写法:
// 用 Fn trait 作为参数类型
fn process<F>(nums: Vec<i32>, f: F)
where
F: Fn(i32) -> i32
{
for n in nums {
println!("{}", f(n));
}
}
fn main() {
let numbers = vec![, , , , ];
process(numbers, |x| x * );
}
// ❌ 这样写不行
fn make_adder(x: i32) -> |i32| -> i32 {
|y| x + y
}
正确写法:
// 用 impl Trait
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
// 或者用 Box(需要堆分配)
fn make_adder_boxed(x: i32) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |y| x + y)
}
fn main() {
let numbers = vec![, , , , , , , , , ];
// 链式调用:过滤偶数 -> 平方 -> 收集
let result: Vec<i32> = numbers
.iter()
.filter(|&n| n % == ) // 闭包 1:过滤
.map(|&n| n * n) // 闭包 2:转换
.collect();
println!("{:?}", result); // [4, 16, 36, 64, 100]
}
看到没?两个闭包链式调用,代码像流水线一样清晰!
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
fn main() {
let mut people = vec![
Person { name: String::from("Alice"), age: },
Person { name: String::from("Bob"), age: },
Person { name: String::from("Charlie"), age: },
];
// 按年龄排序
people.sort_by(|a, b| a.age.cmp(&b.age));
// 按名字长度排序
people.sort_by(|a, b| a.name.len().cmp(&b.name.len()));
for p in &people {
println!("{}: {}", p.name, p.age);
}
}
fn main() {
let expensive_calculation = || {
println!("正在计算...");
// 模拟耗时操作
std::thread::sleep(std::time::Duration::from_secs());
};
// 闭包还没执行
println!("准备调用闭包...");
// 现在才执行
let result = expensive_calculation();
println!("结果:{}", result);
}
闭包可以延迟执行,等到真正需要结果的时候才计算。

金句回顾:
下篇预告:
闭包和迭代器是绝配!下篇咱们深入聊聊迭代器,看看怎么用闭包把数据处理玩出花来。你会爱上 iter().map().filter().collect() 这个套路的!