rust如何在类中保存一个闭包,它可以捕获类自己?

首先这个为什么在 Rust 中这么难实现?

我们得承认一个残酷的现实:Rust 不是 C++。在 C++ 中,可以随心所欲地使用引用,编译器不会过多干涉。但在 Rust 中,编译器会像严厉的监管者一样盯着我们的每一个动作,生怕我们搞出什么内存安全问题。

struct Args {callback: Option<Box<dyn Fn()>>,  // 这样写是不行的!
}impl Args {fn add_help(&mut self) {self.callback = Some(Box::new(|| {self.print_help();  // 编译器: 不行!这里可能会有循环引用!}));}
}

编译器会义正言辞地告诉你:「不行!这样可能会造成循环引用!

柳暗花明又一村

但别灰心!Rust 虽然严格,但也提供了解决方案。这里有几种方法:

1.使用 Rc 和 RefCell 组合拳

use std::rc::Rc;
use std::cell::RefCell;struct Args {callback: Option<Box<dyn Fn()>>,
}impl Args {fn new() -> Rc<RefCell<Args>> {let args = Rc::new(RefCell::new(Args {callback: None,}));let args_clone = args.clone();args.borrow_mut().callback = Some(Box::new(move || {args_clone.borrow().print_help();}));args}
}

2.使用 raw pointer(不推荐,但是可行)

struct Args {callback: Option<Box<dyn Fn()>>,
}impl Args {fn new() -> Args {let mut args = Args {callback: None,};let args_ptr = &args as *const Args;args.callback = Some(Box::new(move || {unsafe {(*args_ptr).print_help();}}));args}
}

3.重新设计 API(推荐)

struct Args {callback: Option<Box<dyn Fn(&Args)>>,  // 把 self 作为参数传入
}impl Args {fn add_help(&mut self) {self.callback = Some(Box::new(|args| {args.print_help();}));}
}

所以最佳实践是什么?

如果你问我推荐哪种方案,我会毫不犹豫地选择第三种。为什么?

  1. 设计更清晰:明确表达了闭包需要操作的对象
  2. 更符合 Rust 哲学:所有权关系一目了然
  3. 代码更安全:没有使用 unsafe 代码
  4. 性能更好:避免了额外的运行时开销

最后最后

其实这个问题反映了一个更深层的设计理念:

在 Rust 中,我们应该顺应语言的特性来设计 API,而不是硬搬其他语言的模式。就像古人说的:顺势而为,事半功倍。

所以下次当你遇到类似问题时,不妨先问问自己:是不是可以换个角度思考这个问题?也许答案就转角处等着你。在 Rust 中,最优雅的解决方案往往不是强行模仿其他语言的模式,而是遵循 Rust 的独特哲学。

如果这篇文章对你有帮助,别忘了点个~ 我是旷野,探索无尽技术!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/66675.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑马跟学.苍穹外卖.Day04

黑马跟学.苍穹外卖.Day04 苍穹外卖-day04课程内容1. Redis入门1.1 Redis简介1.2 Redis下载与安装1.2.1 Redis下载1.2.2 Redis安装 1.3 Redis服务启动与停止1.3.1 服务启动命令1.3.2 客户端连接命令1.3.3 修改Redis配置文件1.3.4 Redis客户端图形工具 2. Redis数据类型2.1 五种常…

SOLID原则学习,开闭原则

文章目录 1. 定义2. 开闭原则的详细解释3. 实现开闭原则的方法4. 总结 1. 定义 开闭原则&#xff08;Open-Closed Principle&#xff0c;OCP&#xff09;是面向对象设计中的五大原则&#xff08;SOLID&#xff09;之一&#xff0c;由Bertrand Meyer提出。开闭原则的核心思想是…

filebeat、kafka

elk的架构 es数据库&#xff1a;非关系型数据库&#xff0c;json格式 logstash&#xff1a;收集日志 kibana&#xff1a;图形化的工具 ↓ 以上三种结合起来即为日志收集系统 filebeat 作用&#xff1a;filebeat是一款轻量级的日志收集工具&#xff0c;不依赖java环境&…

Qt重写webrtc的demo peerconnection

整个demo为&#xff1a; 可以选择多个编码方式&#xff1a; cmake_minimum_required(VERSION 3.5)project(untitled LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_INCLUDE_CURRENT_DIR ON)set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON)set(CMA…

【Notepad++】Notepad++如何删除包含某个字符串所在的行

Notepad如何删除包含某个字符串所在的行 一&#xff0c;简介二&#xff0c;操作方法三&#xff0c;总结 一&#xff0c;简介 在使用beyoundcompare软件进行对比的时候&#xff0c;常常会出现一些无关紧要的地方&#xff0c;且所在行的内容是变化的&#xff0c;不方便进行比较&…

python无需验证码免登录12306抢票 --selenium(2)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 [TOC](python无需验证码免登录12306抢票 --selenium(2)) 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 就在刚刚我抢的票&#xff1a;2025年1月8日…

CSS 盒模型

盒模型 CSS盒模型是网页布局的核心概念之一&#xff0c;它描述了网页元素的物理结构和元素内容与周围元素之间的关系。根据W3C规范&#xff0c;每个HTML元素都被视为一个矩形盒子&#xff0c;这个盒子由以下四个部分组成&#xff1a; 内容区&#xff08;Content area&#xff…

Kivy App开发之UX控件Slider滑块

在app中可能会调节如音量,亮度等,可以使用Slider来实现,该控件调用方便,兼容性好,滑动平稳。在一些参数设置中,也可以用来调整数值。 支持水平和垂直方向,可以设置默认值,最小及最大值。 使用方法,需用引入Slider类,通过Slider类生成一个滑块并设置相关的样式后,再…

WPF的自定义控件控件学习

引入自定义控件 <controls:Intellibox Style"{StaticResource ListSearch-SearchIntellibox}" Width"95" Margin"0,3" MaxResults"200" …

MySQL常用命令之汇总(Summary of Commonly Used Commands in MySQL)

MySQL常用命令汇总 简介 ‌MySQL是一个广泛使用的开源关系型数据库管理系统&#xff0c;由瑞典的MySQL AB公司开发&#xff0c;现属于Oracle公司。‌ MySQL支持SQL&#xff08;结构化查询语言&#xff09;&#xff0c;这是数据库操作的标准语言&#xff0c;用户可以使用SQL进…

【含开题报告+文档+PPT+源码】基于springboot的农贸菜市场租位管理系统的设计与实现

开题报告 随着信息技术的快速发展和普及&#xff0c;信息化管理已成为各行业提升运营效率和服质量的重要手段。农贸菜市场作为城市生活的重要组成部分&#xff0c;其管理效率和服务水平直接关系到市民的日常生活体验。传统的农贸菜市场租位管理方式往往存在信息不对称、管理效…

华为路由器、交换机、AC、新版本开局远程登录那些坑(Telnet、SSH/HTTP避坑指南)

关于华为设备远程登录配置开启的通用习惯1、HTTP/HTTPS相关服务 http secure-server enablehttp server enable 2、Telnet服务telnet server enable3、SSH服务stelnet server enablessh user admin authentication-type password 「模拟器、工具合集」复制整段内容 链接&…

Vue 2 深度剖析:从理论到实战的全面指南

Vue 2 深度剖析&#xff1a;从理论到实战的全面指南 一、引言 在当今前端开发领域&#xff0c;Vue.js 2 版本以其简洁优雅、易于上手且高效强大的特性&#xff0c;广泛应用于各类项目之中&#xff0c;成为众多开发者的心头好。无论是小型创业公司的快速迭代项目&#xff0c;还…

【论文阅读-思维链的构造方法02】4.1.2 Automatic Construction-02

提示1&#xff1a;本篇博客中涉及4篇相关论文&#xff0c;预计阅读时间10分钟&#xff0c;望各位友友耐心阅读&#xff5e; 提示2&#xff1a;本篇所有涉及的论文已打包发布&#xff0c;不需要任何积分即可下载&#xff0c;指路 --> 论文集下载地址 大模型技术-思维链CoT …

【GIt原理与使用】Git远程仓库

一、理解分布式版本控制系统 我们目前所说的所有内容&#xff08;工作区&#xff0c;暂存区&#xff0c;版本库等等&#xff09;&#xff0c;都是在本地&#xff01;也就是在你的笔记本或者计算机上。而我们的 Git 其实是分布式版本控制系统&#xff01;什么意思呢&#xff1f…

【后端面试总结】设计一个分布式锁需要考虑哪些东西

分布式锁是我们在分布式场景中经常用到的一种技术&#xff0c;在后端面试中也是出镜率很高&#xff0c;那么我们设计分布式锁的时候应该从那几方面去考虑呢 实现分布式锁需要考虑的点 设置超时时间 设置超时时间的目的是为了避免这个场景&#xff1a;进程A拿了锁&#xff0c…

javascript e.preventDefault() 的作用和用法

&#x1f4da; e.preventDefault() 的作用和用法 ✅ e.preventDefault() 是一个常用的 JavaScript 方法&#xff0c;用于 阻止事件的默认行为。它通常在 表单提交、链接跳转、右键菜单 等场景中使用&#xff0c;防止浏览器执行特定的默认操作。 &#x1f50e; 1. 为什么使用 e…

如何在 Docker 中切换登录用户

在 Docker 中进行身份验证时&#xff0c;通常是使用 Docker Hub 或其他私有仓库。如果你希望在同一仓库地址上切换不同的用户进行登录&#xff0c;以下是详细的操作步骤。 1. 退出当前用户 首先&#xff0c;使用 docker logout 命令退出当前用户的登录状态。这个操作会清除 D…

力扣-数组-88 合并两个有序数组

解析 分别维护指向两个数组的指针&#xff0c;不断往后增加指针即可&#xff0c;主要是边界&#xff0c;然后时间复杂度是。 代码 class Solution { public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {vector <int> new…

家用万兆网络实践:紧凑型家用服务器静音化改造(二)

大家好&#xff0c;这篇文章我们继续分享家里网络设备的万兆升级和静音改造经验&#xff0c;希望对有类似需求的朋友有所帮助。 写在前面 在上一篇《家用网络升级实践&#xff1a;低成本实现局部万兆&#xff08;一&#xff09;》中&#xff0c;我们留下了一些待解决的问题。…