闭包
# Fn、FnMut 和 FnOnce
在 Rust 中,Fn
、FnMut
和 FnOnce
是三个 trait(特性),它们用于描述闭包(Closure)如何捕获和使用其环境中的变量。 编译器会根据闭包内部的行为自动为其实现这三个 trait 中最合适的一个。
这三个 trait 的核心区别在于它们如何与捕获的变量交互,这直接决定了闭包可以被调用多少次。
# 1. FnOnce
:消费所有权(最多调用一次)
- 含义:
Once
(一次)意味着这类闭包最多只能被调用一次。 - 捕获方式:它通过获取环境中变量的所有权(taking ownership)来捕获它们。一旦闭包被调用,它会消费掉(consume)这些变量,导致所有权被移出闭包,因此闭包本身也无法再次被调用。
self
类型:可以类比为接收self
参数的方法,它会消耗掉实例本身。
使用场景:
当你需要在闭包内部转移捕获变量的所有权时,例如,将一个 String
移动到一个新的线程中。
示例:
fn main() {
let s = String::from("hello");
// `move` 关键字强制闭包获取 s 的所有权
let consume_s = move || {
// s 的所有权在这里被消耗
println!("s is: {}", s);
// std::mem::drop(s); // 显式地丢弃 s
};
consume_s();
// 下面这行代码会编译失败,因为 s 的所有权已经被移动到闭包中,
// 并且在调用 consume_s() 后被销毁。
// println!("Cannot use s here: {}", s);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2. FnMut
:可变借用(可多次调用,可修改环境)
- 含义:
Mut
(mutable)意味着这类闭包可以修改其捕获的环境变量。 - 捕获方式:它以可变借用 (
&mut T
) 的方式捕获环境变量。因为只是借用,所以闭包可以被多次调用。 self
类型:可以类比为接收&mut self
参数的方法,允许修改实例的状态。
使用场景: 当你需要一个可以被多次调用,并且每次调用都会改变其状态的闭包时,比如一个计数器。
示例:
fn main() {
let mut count = 0;
// 这个闭包需要修改 count 的值,所以它是一个 FnMut
let mut increment = || {
count += 1;
println!("Count is now: {}", count);
};
increment(); // 输出: Count is now: 1
increment(); // 输出: Count is now: 2
// 可以在闭包外部再次访问 count,因为它只是被可变借用了
println!("Final count: {}", count); // 输出: Final count: 2
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 3. Fn
:不可变借用(可多次并发调用,只读环境)
- 含义:这是限制最严格的闭包类型,它只能读取环境变量,不能修改它们。
- 捕获方式:它以不可变借用 (
&T
) 的方式捕获环境变量。因为是不可变借用,所以它可以被多次调用,甚至可以安全地在多个线程之间并发调用。 self
类型:可以类比为接收&self
参数的方法,只能读取实例的状态。
使用场景: 当你只需要读取环境中的数据,而不需要修改它时。这是最灵活的闭包类型。
示例:
fn main() {
let message = String::from("hello");
// 这个闭包只是读取 message,所以它是一个 Fn
let print_message = || {
println!("Message is: {}", message);
};
print_message();
print_message();
// 在闭包之后,message 仍然完全可用
println!("Message after closure: {}", message);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 总结与层级关系
这三个 trait 之间存在一个层级关系:
Fn
⊂ FnMut
⊂ FnOnce
这意味着:
- 所有实现了
Fn
的闭包,也自动实现了FnMut
和FnOnce
。 - 所有实现了
FnMut
的闭包,也自动实现了FnOnce
。
换句话说,如果你有一个需要 FnOnce
类型闭包的函数,你可以给它传递一个 Fn
或 FnMut
类型的闭包,因为它们都可以被“调用一次”。但反过来则不行,例如,你不能把一个 FnOnce
闭包传递给一个要求 Fn
的函数,因为它可能会被多次调用。
Trait | 捕获方式 | 调用次数 | 修改环境 | self 类比 |
---|---|---|---|---|
Fn | 不可变借用 (&T ) | 多次 (可并发) | 否 (只读) | &self |
FnMut | 可变借用 (&mut T ) | 多次 (不可并发) | 是 | &mut self |
FnOnce | 获取所有权 (T ) | 最多一次 | 是/否 | self |
上次更新: 2025/08/19, 08:47:47