当前位置: 首页 > news >正文

恋爱脑学Rust之闭包三Traits:Fn,FnOnce,FnMut

在这里插入图片描述

在Rust中,FnOnce、FnMut和Fn是三个用于表示闭包(closure)类型的trait。闭包是一种特殊的函数,它可以捕获其环境变量,即在其定义时所处的作用域中的变量。以下是关于这三个trait的详细介绍:


1. FnOnce:一生一次的承诺

理解FnOnce 就像在爱情中那个“一诺千金”的承诺。它只能被调用一次,付出了就没有回头路。我们在 Rust 中通常会用 FnOnce 处理那些需要“独占”资源的闭包,因为它会拿走所有权。

实际应用场景

比如你设计一个“任务系统”,只允许任务被执行一次。这时候可以用 FnOnce 确保执行后资源不再使用。

代码示例
fn execute_task<F>(task: F)
whereF: FnOnce() -> String,
{println!("任务开始:{}", task());println!("任务完成!");
}fn main() {let farewell = String::from("离别时的承诺——我会一直记得你。");// `FnOnce` 闭包只能调用一次execute_task(move || farewell)
}

在这个例子中,execute_task 函数接受一个 FnOnce 闭包,并调用一次。因为闭包拿走了 farewell 的所有权,所以调用后就不再拥有它了。这种设计在需要“唯一性”的任务或资源独占场景中特别有用。

唯一性任务或资源独占是举个例子

在需要资源独占和“一次性任务”的设计中,通常是因为某些操作会消耗资源、改变状态或生成副作用,这些任务在执行后不应被再次调用。典型场景包括文件的独占写入、数据库的唯一记录生成、网络连接的关闭等。在这些场景中使用 FnOnce 设计模式可以确保资源被安全地独占并只被使用一次。

** 实际场景:一次性文件写入 **

举个具体例子,假设我们有一个日志系统,该系统在某些操作完成后会生成一次性的报告,并将它写入文件。由于这份报告只能生成一次,并且会消耗一定资源(比如文件句柄、内存等),因此我们可以使用 FnOnce 来确保只调用一次。

use std::fs::File;
use std::io::{self, Write};/// 写入报告的函数,使用 FnOnce 确保资源独占
fn write_report<F>(generate_report: F) -> io::Result<()>
whereF: FnOnce() -> String,
{let mut file = File::create("report.txt")?;let report_content = generate_report(); // 生成并获取报告内容writeln!(file, "{}", report_content)?;Ok(())
}fn main() {// 使用 `FnOnce` 闭包来生成报告内容let report_closure = || {String::from("系统性能报告:\nCPU 使用率:45%\n内存使用率:60%\n...")};// 执行一次性写入if let Err(e) = write_report(report_closure) {eprintln!("报告写入失败: {}", e);} else {println!("报告已成功写入 report.txt。");}// 再次调用 `report_closure` 会导致编译错误// error[E0382]: use of moved value: `report_closure`// 这是因为 `report_closure` 的所有权已经在 `write_report` 中被消耗let another_report = report_closure(); // ❌ 错误:`report_closure` 只能调用一次println!("再次生成的报告: {}", another_report);
}

注释与解释

  1. 首次调用 report_closurereport_closurewrite_report 函数内部被调用,这是 FnOnce 闭包的首次使用,也是唯一一次符合所有权规则的调用。
  2. 再次调用 report_closure:尝试再次调用 report_closure 时,Rust 编译器会产生错误 error[E0382]: use of moved value,因为 report_closure 的所有权在 write_report 中被完全消耗,因此无法再次使用。

关键点

  • 一次性调用保证:Rust 强制执行 FnOnce 闭包的“一次性调用”规则,避免了资源重复使用,确保了一次性任务的安全性和数据正确性。
  • 防止数据不一致:在一些操作中(如文件生成、系统操作等),数据只能生成一次,防止重复调用的错误帮助减少潜在的并发问题或数据不一致问题。

2. FnMut:随着时光的推移而改变

理解FnMut 代表着随着时光的推移而不断变化的爱。我们会随着岁月逐渐改变,但初心依旧。在 Rust 中,FnMut 闭包可以多次调用,每次调用都允许内部状态发生变化,比如记录次数、更新变量等。

实际应用场景

你可以使用 FnMut 来创建一个计数器,记录事件的发生次数。比如在按钮被按下时,每次记录点击次数,这就是 FnMut 的用武之地。

代码示例
fn count_visits<F>(mut visit: F)
whereF: FnMut() -> String,
{for _ in 0..3 {println!("{}", visit());}
}fn main() {let mut visits = 0;let mut track_visit = move || {visits += 1;format!("这是第{}次见到你了!", visits)};count_visits(track_visit);
}

在这个例子中,每次调用 track_visit 都会更新 visits 的值。因为我们需要修改闭包内部的状态(计数次数),所以选择了 FnMut,它在被多次调用时仍能更新变量。


3. Fn:恒久不变的守护

理解Fn 是那个始终不变的承诺,日复一日的陪伴,永远如一。Fn 闭包不会改变内部状态,能够多次调用并始终如初。在 Rust 中,Fn 是适用于无状态或线程安全的并发计算,因为它不会持有可变数据。

实际应用场景

假如你需要创建一个多次使用的计算函数,比如返回固定信息的服务。Fn 就很适合,既不会占用资源,又保证线程安全。

代码示例
fn repeat_message<F>(message: F)
whereF: Fn() -> String,
{for _ in 0..3 {println!("{}", message());}
}fn main() {let phrase = String::from("我会一直陪伴你。");let say_always = || phrase.clone(); // 使用 `Fn` 闭包,不会改变任何状态repeat_message(say_always);
}

在这里,每次调用 say_always 都会输出同样的内容,因为闭包没有持有任何可变状态。在并发场景中,Fn 也可以安全地在线程中共享,适用于那些需要持续执行同一任务的情况。


总结

  • FnOnce:一次性的承诺,适合需要独占资源的场景;
  • FnMut:会随着时间变化的承诺,适合多次调用且需要更新状态的情况;
  • Fn:恒久不变的陪伴,适合需要线程安全、状态不变的计算或服务。

这些不同的承诺类型让我们在 Rust 中设计灵活又安全的闭包调用方式,从而更好地控制资源和状态。

相关文章:

恋爱脑学Rust之闭包三Traits:Fn,FnOnce,FnMut

在Rust中&#xff0c;FnOnce、FnMut和Fn是三个用于表示闭包&#xff08;closure&#xff09;类型的trait。闭包是一种特殊的函数&#xff0c;它可以捕获其环境变量&#xff0c;即在其定义时所处的作用域中的变量。以下是关于这三个trait的详细介绍&#xff1a; 1. FnOnce&#…...

区块链介绍

区块链&#xff08;英文名&#xff1a;blockchain或block chain&#xff09;是一种块链式存储、不可篡改、安全可信的去中心化分布式账本&#xff0c;它结合了分布式存储、点对点传输、共识机制、密码学等技术&#xff0c;通过不断增长的数据块链&#xff08;Blocks&#xff09…...

git回滚间隔的提交

如果你需要回滚几个非连续的提交&#xff0c;可以使用 git revert 来选择性地撤销这些提交。这样做不会改变提交历史&#xff0c;只是会在当前分支上创建新的提交来反转指定的更改。 ### 使用 git revert 回滚间隔的提交 1. **查看提交历史**&#xff1a; 首先&#xff0c…...

Map和Set(数据结构)

一、概念 Map 和 set 是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例化子类有关。 Map 和 Set 是一种适合动态查找的集合容器。 模型 一般把搜索的数据称为关键字&#xff08; Key &#xff09;&#xff0c;和关键字对应的称为值&#xff0…...

vue3uniapp实现自定义拱形底部导航栏,解决首次闪烁问题

前言&#xff1a; 我最初在网上翻阅查找了很多方法&#xff0c;发现大家都是说在page.json中tabbar中添加&#xff1a;"custom": true,即可解决首次闪烁的问题&#xff0c;可是添加了我这边还是会闪烁&#xff0c;因此我这边改变了思路&#xff0c;使用了虚拟页面来解…...

新需求编码如何注意低级错误代码

1. 日常开发常见错误问题 变量拷贝未修改变量定义的值刚开始是随意写的一个值&#xff0c;想等到上线的时候再改成正确的&#xff0c;但是上线的时候忘记改了程序常量配置的错误逻辑关系判断错误 常见的如都不为null、都不为空集合判断不为空逻辑取反了多个关系的 && …...

系统架构图设计(行业领域架构)

物联网 感知层&#xff1a;主要功能是感知和收集信息。感知层通过各种传感器、RFID标签等设备来识别物体、采集信息&#xff0c;并对这些信息进行初步处理。这一层的作用是实现对物理世界的感知和初步处理&#xff0c;为上层提供数据基础网络层&#xff1a;网络层负责处理和传输…...

windows 文件监控 c++ 11及以上版本可用

在该版本上稍微改了一下https://blog.csdn.net/weixin_50964512/article/details/125002563 #include<iostream> #include<string> #include<Windows.h> #include<list> #include<locale> using namespace std;class WatchFolder {HANDLE m_hFi…...

jsMind:炸裂项目,用JavaScript构建的思维导图库,GitHub上的热门开源项目

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和工作学习方法 jsMind 是一个基于 JavaScript 的思维导图库&#xff0c;它利用 HTML5 Canvas 和 SVG 技术构建&#xff0c;可以轻松地在网页中嵌入和编辑思维导图。它以 …...

postman的脚本设置接口关联

pm常用的对象 变量基础知识 postman获取响应结果的脚本的编写 下面是购物场景存在接口信息的关联 登录进入---搜索商品---进入商品详情---加入购物车 资源在附件中&#xff0c;可以私聊单独发送 postman的SHA256加密 var CryptoJS require(crypto-js);// 需要加密的字符串 …...

【python】OpenCV—Tracking(10.3)—GOTURN

文章目录 1、功能描述2、模型介绍3、代码实现4、完整代码5、结果展示6、优缺点分析7、参考 1、功能描述 基于 Generic Object Tracking using Regression Networks 方法&#xff0c;实现单目标跟踪 2、模型介绍 &#xff08;1&#xff09;发表来自 Held D, Thrun S, Savarese…...

git pull遇到一个问题

shell request failed on channel 0 需要修改服务器配置[rootadmin ~]# cat /etc/security/limits.d/20-nproc.conf # Default limit for number of users processes to prevent # accidental fork bombs. # See rhbz #432903 for reasoning.* soft nproc 409…...

书生-第四期闯关:完成SSH连接与端口映射并运行hello_world.py

端口映射完成后&#xff0c;访问127.0.0.1&#xff1a;7860成功展示如下界面&#xff1a; 书生浦语大模型实战营 项目地址&#xff1a;https://github.com/InternLM/Tutorial/...

【CSS3】css开篇基础(5)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…...

AI产品独立开发变现实战营,炒掉老板做自由职业赚大钱

课程背景 在经济下行和外部就业压力增大的背景下&#xff0c;为解决程序员的焦虑、失业和被裁员&#xff0c;我们开始了这门课程&#xff0c;课程基于3个真实已经盈利的商业项目&#xff0c;从0到1带你实践AI产品的设计、开发、运营和盈利模式的全流程开发。 课程特色 增加‘…...

【UE5.3 Cesium for Unreal】编译GlobePawn

目录 前言 效果 步骤 一、下载所需文件 二、下载CesiumForUnreal插件 三、处理下载的文件 四、修改代码 “CesiumForUnreal.uplugin”部分 “CesiumEditor.cpp”部分 “CesiumEditor.h”部分 “CesiumPanel.cpp”部分 “IonQuickAddPanel.cpp”部分 “IonQuickAd…...

idea连接数据库出现错误的解决方式

在使用idea连接数据库时&#xff0c;出现错误&#xff1a; The server has terminated the handshake. The protocol list option (enabledTLSProtocols) is set, this option might cause connection issues with some versions of MySQL. Consider removing the protocol li…...

数据分级分类工具:敏感数据识别中的AI智能化转型之路

背景 在现代数字化和信息化飞速发展的背景下&#xff0c;数据安全愈发成为企业与组织的重要课题&#xff0c;尤其是敏感数据的保护更是重中之重。敏感数据的泄露不仅会导致商业损失和法律责任&#xff0c;还会直接影响客户信任和企业声誉。为此&#xff0c;数据分级分类工具逐…...

乘云而上,OceanBase再越山峰

一座山峰都是一个挑战&#xff0c;每一次攀登都是一次超越。 商业数据库时代&#xff0c;面对国外数据库巨头这座大山&#xff0c;实现市场突破一直都是中国数据库产业多年夙愿&#xff0c;而OceanBase在金融核心系统等领域的攻坚克难&#xff0c;为产业突破交出一副令人信服的…...

设计模式4-工厂模式策略模式

目录 一 工厂模式 1.1 思想 1.2 案例 1.2.1 接口 1.2.2 实现类 1.2.3 工厂类 1.2.4 调用 二 策略模式 2.1 思想 2.2 案例 2.2.1 接口 2.2.2 实现类 2.2.3 策略类 2.2.4 调用 三 工厂模式策略模式 3.1 思想 3.2 案例 3.2.1 接口 3.2.2 实现类 3.2.3 定义F…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...