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

在 Rust 中,FnFnMutFnOnce 是三个用于表示闭包类型的 trait。闭包是一种可以捕获其环境变量的函数。在创建闭包是会默认实现这几个 trait 中的一个。

以下是三个 trait 的区别:

  • FnFn 是最基本的闭包 trait。它表示闭包可以捕获其环境变量的不可变引用。

  • FnMutFnMut 表示闭包可以捕获其环境变量的可变引用。这意味着闭包可以修改其环境变量的值。

  • FnOnceFnOnce 表示闭包只能调用一次。它表示闭包可以捕获其环境变量的所有权。这意味着闭包可以移动其环境变量的值。

以下是三个 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。

此外,FnFnMutFnOnce 还可以相互转换。

  • Fn 可以转换为 FnMutFnOnce
  • 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。

总结:

FnFnMutFnOnce 是三个用于表示闭包类型的 trait。选择哪个 trait 取决于闭包的用途。此外,FnFnMutFnOnce 还可以相互转换。

我正在编写《Rust 精华小册》,这是一个免费专栏,有需要的朋友可以自助获取哦,也可以关注我的公众号"程序饲养员"获取最新动态。

相关推荐
阿珊和她的猫27 分钟前
v-scale-scree: 根据屏幕尺寸缩放内容
开发语言·前端·javascript
uzong4 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
加班是不可能的,除非双倍日工资5 小时前
css预编译器实现星空背景图
前端·css·vue3
桦说编程5 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研5 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip6 小时前
vite和webpack打包结构控制
前端·javascript
excel6 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端