闭包
约 402 字大约 1 分钟
2025-12-03
重要
闭包是能捕获其定义时外部作用域值的匿名函数,被捕获的值会随闭包一起存在,按需借用或移动
闭包的语法为 |params| expr 或 |params| { block }。参数与返回类型通常可自动推断;若需显式声明(例如作为函数参数或返回值)再标注类型。
fn main() {
let x = 10;
let add = |y: i32| x + y;
let sum = |a: i32, b: i32| a + b;
assert_eq!(add(5), 15);
println!("sum is {}", sum(1, 2));
}捕获与所有权
闭包可以通过三种方式捕获其环境中的值,分别为:
- 只读借用实现
Fn - 可变借用实现
FnMut - 按值移动实现
FnOnce
捕获何时发生取决于闭包是否调用,以及是否用 move 强制按值捕获。
只读借用
fn main() {
let prefix = "Hello, ".to_string();
// 只读借用 prefix,不可变捕获,闭包实现 Fn
let greet = |name: &str| format!("{prefix}{name}");
println!("{}", greet("Rust"));
println!("{}", greet("World"));
// prefix 仍可读用
println!("prefix still here: {prefix}");
}可变借用
fn main() {
let mut count = 0;
// 可变借用 count,闭包是 FnMut
let mut inc = || { count += 1; };
inc();
inc();
assert_eq!(count, 2);
}按值移动
fn main() {
let v = vec![1, 2, 3];
// move 强制按值捕获 v,闭包至少是 FnOnce
let consume = move || {
// v 被移动进来,这里取得所有权
for x in v {
print!("{x} ");
}
};
consume();
// consume 再调用会编译错误,因为 v 已被消耗
}注
如果签名接受 FnOnce,那么任何闭包都能适配;接受 FnMut 则要求闭包不消耗环境;接受 Fn 则要求只读捕获