当前位置: 首页 > 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…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...