Rust中的Fn、FnMut 和 FnOnce都有什么区别?

在 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 精华小册》,这是一个免费专栏,有需要的朋友可以自助获取哦,也可以关注我的公众号"程序饲养员"获取最新动态。

相关推荐
蜗牛互联网19 分钟前
Java 17 HttpClient调用文件转写API的超时与失败回退
java·人工智能·后端
Csvn1 小时前
框架性能优化
前端
回眸&啤酒鸭2 小时前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
用户69371750013843 小时前
2026,程序员的时代拐点到了
android·前端·后端
大龄秃头程序员3 小时前
一次 iBeacon + BLE 无感解锁方案的实现记录
前端
惜鸟3 小时前
从源码看 pi 的上下文管理:原始数据永久保留,模型视角按需裁剪
后端
小兔子3 小时前
Python 的 GIL 与 free-threading:3.13 之后「去 GIL」走到哪一步了
前端
量化分析码农3 小时前
【Python量化策略评估体系 #05】极端行情扛得住吗?2008/2015/2020 三场景压力测试
后端
十年Java程序媛3 小时前
Java 接口和抽象类对比|Java8 新特性,抛弃老旧八股,正确选型
java·spring boot·后端
IT_陈寒3 小时前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端