在 Rust 中,Fn
、FnMut
和 FnOnce
是三个用于表示闭包类型的 trait。闭包是一种可以捕获其环境变量的函数。在创建闭包是会默认实现这几个 trait 中的一个。
以下是三个 trait 的区别:
-
Fn :
Fn
是最基本的闭包 trait。它表示闭包可以捕获其环境变量的不可变引用。 -
FnMut :
FnMut
表示闭包可以捕获其环境变量的可变引用。这意味着闭包可以修改其环境变量的值。 -
FnOnce :
FnOnce
表示闭包只能调用一次。它表示闭包可以捕获其环境变量的所有权。这意味着闭包可以移动其环境变量的值。
以下是三个 trait 的一些示例:
rust
// Fn
let add_one_closure = |x: i32| -> i32 {
let y = 10;
x + y
};
// FnMut
let add_two_closure = |mut x: i32| -> i32 {
x += 2;
x
};
// FnOnce
let consume_string_closure = |s: String| -> String {
println!("Consumed string: {}", s);
s
};
输出结果:
c
Consumed string: Hello, world!
总结:
Fn
:闭包可以捕获其环境变量的不可变引用。FnMut
:闭包可以捕获其环境变量的可变引用。FnOnce
:闭包只能调用一次,并可以捕获其环境变量的所有权。
选择哪个 trait 取决于闭包的用途。
- 如果闭包只需要读取环境变量的值,则可以使用
Fn
trait。 - 如果闭包需要修改环境变量的值,则可以使用
FnMut
trait。 - 如果闭包只需要使用环境变量的值一次,则可以使用
FnOnce
trait。
以下是一些使用建议:
- 优先使用
Fn
trait,因为它是最通用的。 - 只有在需要修改环境变量的值时才使用
FnMut
trait。 - 只有在需要使用环境变量的值一次时才使用
FnOnce
trait。
此外,Fn
、FnMut
和 FnOnce
还可以相互转换。
Fn
可以转换为FnMut
和FnOnce
。FnMut
可以转换为FnOnce
。
以下是转换示例:
rust
// Fn -> FnMut
fn add_one(x: i32) -> i32 {
let y = 10;
x + y
}
let mut add_one_fnmut: FnMut(i32) -> i32 = add_one;
// FnMut -> FnOnce
fn add_two(mut x: i32) -> i32 {
x += 2;
x
}
let add_two_fnonce: FnOnce(i32) -> i32 = add_two;
从源码的角度看它们为什么能够转换:
rust
pub trait FnOnce<Args: Tuple> {
...
}
pub trait FnMut<Args: Tuple>: FnOnce<Args> {
...
}
pub trait Fn<Args: Tuple>: FnMut<Args> {
...
}
从源码可以看出,Fn继承了FnMut,而FnMut又继承了FnOnce。这是类型逐渐泛化的过程,这就好像坤坤属于男人,男人属于灵长类一个道理。 Fn要求只能只能捕获环境变量,而FnMut不但捕获环境变量还需要修改环境变量,所以Fn一定能满足FnMut的条件。就像1是正整数,那么它必然也是整数。同样的,FnOnce要求拿到环境变量的所有权,如此一来,不管是Fn还是FnMut都是在没有所有权的基础上运行的,有了所有权那就是能无所无能了。因此,无论是Fn还是FnMut都可以转换为FnOnce。
总结:
Fn
、FnMut
和 FnOnce
是三个用于表示闭包类型的 trait。选择哪个 trait 取决于闭包的用途。此外,Fn
、FnMut
和 FnOnce
还可以相互转换。
我正在编写《Rust 精华小册》,这是一个免费专栏,有需要的朋友可以自助获取哦,也可以关注我的公众号"程序饲养员"获取最新动态。