Rust 解决循环引用
导航
- 循环引用
- 一、现象
- 二、解决
循环引用
循环引用出现的一个场景就是你指向我,我指向你,导致程序崩溃
解决方式可以通过弱指针,而Rust中的弱指针就是Weak
在Rc中,可以实现,对一个变量,持有多个不可变引用,并且都拥有该变量的所有权,这种情况下就可能会发生循环引用的现象
一、现象
从代码中来,到代码中去,先上个图

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;#[derive(Debug)]
enum List {Cons(i32, RefCell<Rc<List>>),Nil,
}impl List {fn tail(&self) -> Option<&RefCell<Rc<List>>> {match self {Cons(_, item) => Some(item),Nil => None,}}
}
fn main() {//Cons(5, RefCell::new(Rc::new(Nil)))已经有一个拥有者alet a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));println!("a的初始化rc计数 = {}", Rc::strong_count(&a));println!("a指向的节点 = {:?}", a.tail());// 创建`b`到`a`的引用//b指向的结构体Cons(10, RefCell::new(Rc::clone(&a)),结构体字段也拥有了Cons(5, RefCell::new(Rc::new(Nil))//对于Cons(10, RefCell::new(Rc::clone(&a)结构体,只有一个拥有者blet b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));//所以对于Cons(5, RefCell::new(Rc::new(Nil)))已经有了两个拥有者println!("在b创建后,a的rc计数 = {}", Rc::strong_count(&a));//这里是2println!("b的初始化rc计数 = {}", Rc::strong_count(&b));//这里是1println!("b指向的节点 = {:?}", b.tail());// 利用RefCell的可变性,创建了`a`到`b`的引用if let Some(link) = a.tail() {//因为 Cons(i32, RefCell<Rc<List>>)内部定义的是RefCell,可以修改指向,指向另一个Rc*link.borrow_mut() = Rc::clone(&b);//指向b}//对于Cons(10, RefCell::new(Rc::clone(&a)已经有b和a结构体中的Cons字段,两个所有者println!("在更改a后,b的rc计数 = {}", Rc::strong_count(&b));//对于Cons(5, RefCell::new(Rc::clone(&b)已经有a和b结构体中的Cons字段,两个所有者println!("在更改a后,a的rc计数 = {}", Rc::strong_count(&a));// 下面一行println!将导致循环引用//Rc指向的对象被drop的原则是,所有拥有者已经失去所有权,如果不主动drop,就无法解开引用// 下面一行println!将导致循环引用// 我们可怜的8MB大小的main线程栈空间将被它冲垮,最终造成栈溢出,Debug特征会一直打印每个结构体的字段,所以会递归// println!("a next item = {:?}", a.tail());
}
二、解决
Weak 非常类似于 Rc,但是与 Rc 持有所有权不同,Weak 不持有所有权,借用的值可以在生命周期结束后正常drop掉,就不会出现循环引用了.
Weak和Rc不同的地方是:
- 获取一个没有所有权的引用方式是,Rc::downgrade(&)
- 获取借用值是
upgrade(),返回的是Option<Rc> ,值存在就返回引用,不存在就返回None
use std::rc::Rc;
use std::rc::Weak;
use std::cell::RefCell;// 主人
struct Owner {name: String,gadgets: RefCell<Vec<Weak<Gadget>>>,
}// 工具
struct Gadget {id: i32,owner: Rc<Owner>,
}fn main() {// 创建一个 Owner// 需要注意,该 Owner 也拥有多个 `gadgets`let gadget_owner : Rc<Owner> = Rc::new(Owner {name: "Gadget Man".to_string(),gadgets: RefCell::new(Vec::new()),});// 创建工具,同时与主人进行关联:创建两个 gadget,他们分别持有 gadget_owner 的一个引用。let gadget1 = Rc::new(Gadget{id: 1, owner: gadget_owner.clone()});let gadget2 = Rc::new(Gadget{id: 2, owner: gadget_owner.clone()});// 为主人更新它所拥有的工具// 因为之前使用了 `Rc`,现在必须要使用 `Weak`,否则就会循环引用gadget_owner.gadgets.borrow_mut().push(Rc::downgrade(&gadget1));gadget_owner.gadgets.borrow_mut().push(Rc::downgrade(&gadget2));// 遍历 gadget_owner 的 gadgets 字段for gadget_opt in gadget_owner.gadgets.borrow().iter() {// gadget_opt 是一个 Weak<Gadget> 。 因为 weak 指针不能保证他所引用的对象// 仍然存在。所以我们需要显式的调用 upgrade() 来通过其返回值(Option<_>)来判// 断其所指向的对象是否存在。// 当然,Option 为 None 的时候这个引用原对象就不存在了。let gadget = gadget_opt.upgrade().unwrap();println!("Gadget {} owned by {}", gadget.id, gadget.owner.name);}// 在 main 函数的最后,gadget_owner,gadget1 和 gadget2 都被销毁。// 具体是,因为这几个结构体之间没有了强引用(`Rc<T>`),所以,当他们销毁的时候。//!!!!!!// 首先 gadget2 和 gadget1 被销毁。// 然后因为 gadget_owner 的引用数量为 0,所以这个对象可以被销毁了。// 循环引用问题也就避免了
}

相关文章:
Rust 解决循环引用
导航 循环引用一、现象二、解决 循环引用 循环引用出现的一个场景就是你指向我,我指向你,导致程序崩溃 解决方式可以通过弱指针,而Rust中的弱指针就是Weak 在Rc中,可以实现,对一个变量,持有多个不可变引…...
ICC2:如何解决pin density过高引起的绕线问题
我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 为了追求极致的利用率,综合往往会使用大量的AOI/OAI等多pin cell,然而后端实现过程中,工具为了解决绕线难题,又会通过降低local density的方法实现反向奔赴,即便如此,绕线后仍会残留不少问题,…...
Buuctf-Misc题目练习
打开后是一个gif动图,可以使用stegsolve工具进行逐帧看。 File Format:文件格式 Data Extract:数据提取 Steregram Solve:立体试图 可以左右控制偏移 Frame Browser:帧浏览器 Image Combiner:拼图,图片拼接 所以可以知道我们要选这个Frame Browser …...
费马小定理详解
费马小定理 定义: 设 p 为素数,a 为整数,则 a p ≡ a ( m o d p ) a^p \equiv a\ (\mod p) ap≡a (modp) ,若 p ∤ a p \nmid a p∤a ,则 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1\ (\mod p) ap−1≡1 (modp)…...
PXE批量安装
系统装机的三种引导方式 u盘光盘网络装机 光盘: 1.类似于usb模式 2.刻录模式 系统安装过程 加载boot loader Boot Loader 是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从…...
stm32f103c8t6最小系统板
STM32F103C8T6最小系统板是为基于ARM Cortex-M3内核的STM32F103C8T6微控制器设计的电路板,它包含了单片机正常运行所需的最基本组件。以下是构成STM32F103C8T6最小系统板的基本部分: 单片机芯片:STM32F103C8T6本身,它是一款32位微…...
QCefView 在 Linux 下的编译(更新)
在前面的文章《QT 应用程序中集成浏览器》中已经介绍过 QCefView 的构建。这几天发现 QCefView 代码进行了更新,构建方式也发生了一点点变化,所以在此更新一下 QCefView 的编译方法。 QCefView 其实包含了两个项目,一个就是 QCefView 项目本身,另外一个就是 CefViewCore。…...
无卤素产品是什么?有什么作用?
无卤素产品,即在生产过程中完全不使用卤素元素——氟、氯、溴、碘等——的产品。 卤素元素,虽然在电子设备、材料等领域应用广泛,却也可能潜藏危害。其阻燃剂,一旦在产品生命周期结束后释放,将对土壤和水体造成污染&a…...
esp32-cam 1. 出厂固件编译与测试
0. 环境 - ubuntu18 - esp32-cam - usb转ttl ch340 硬件连接 esp32-camch340板子U0RTXDU0TRXDGNDGND5V5V 1. 安装依赖 sudo apt-get install vim sudo apt install git sudo apt-get install git wget flex bison gperf python python-pip python-setuptools python-serial p…...
题目:线性代数
问题描述: 解题思路: 列相乘,然后行相加。 注意点:由于元素数据范围最大为1e6,两个元素相乘乘积最大为1e12,如果元素类型为int则在乘的过程中就会爆炸,所以需要开long long类型。 AC代码…...
docker学习笔记3:VmWare CentOS7安装与静态ip配置
文章目录 一、安装CentOS71、下载centos镜像2、安装二、设置静态ip三、xshell连接centos本专栏的docker环境是在centos7里安装,因此首先需要会安装centos虚拟机。 本篇博客介绍如何在vm虚拟机里安装centos7。 一、安装CentOS7 1、下载centos镜像 推荐清华源,下载如下版本 …...
leetcode 547.省份数量
思路:dfs 或者这道题用bfs也是可以的。 这道题有点迷惑性,这里的数组给的是无向图的数组,而并不是地图,这里需要着重注意一下。 而后,这里的状态数组st没必要是二维的,我们并不会去遍历所给的数组&#…...
Qt5 框架学习及应用 — 对象树
Qt 对象树 对象树概念Qt为什么使用对象树 ?将对象挂到对象树上 对象树概念 对象树:对于树的概念,相信许多学过数据结构的同学应该都不会陌生。在学习数据结构的时候我们所接触的什么二叉树、多叉树、哈夫曼树、AVL树、再到红黑树、B/B树………...
Ansible自动化运维工具---Playbook
一、playbook playbook是剧本的意思 通过 task 调用 ansible 的模块将多个 play 组织在一 个playbook中运行。 playbook本身由以下各部分组成: Tasks: 任务,即调用模块完成的某操作Variables: 变量Templates: 模板Handlers: 处理器,当某条…...
什么是接口和类?Java中的集合框架有哪些主要接口和类?
Java中的集合框架有哪些主要接口和类? Java中的集合框架(Java Collections Framework)提供了一套丰富的接口和类,用于存储和操作对象的集合。以下是Java集合框架中的主要接口和类: 主要接口 Collection: 这…...
算法学习笔记(最短路——Bellman-Ford)
B e l l m a n — F o r d Bellman—Ford Bellman—Ford是一种单源最短路径算法,可以用于边权为负的图,但是只能用于小图。 大概过程: 枚举每一条边,更新可以更新的节点(起点到自己距离为 0 0 0,从地点开…...
try-catch-finally的省略与springboot
在 Java 中,try-catch 块是用于捕获和处理异常的结构,它可以帮助您在代码中处理可能发生的异常情况。在某些情况下,您可能希望省略 try-catch 块并将异常向上抛出,让调用者处理异常。这种情况通常适用于以下情况: 方法…...
容器Docker:轻量级虚拟化技术解析
引言 随着云计算和虚拟化技术的飞速发展,容器技术以其轻量级、高效、可移植的特性,逐渐成为了软件开发和部署的新宠。在众多容器技术中,Docker以其简单易用、功能强大的特点,赢得了广泛的关注和应用。本文将全面介绍Docker的基本概…...
windows 系统中cuda 12.1 环境安装
文章目录 1. 安装cuda 12.11.1 下载1.2 安装 cuda1.2.1 安装步骤1.2.2 环境变量安装1.3 安装cuDNN1.3.1 安装1.3.2 cuDNN配置验证2. anaconda 安装2.1 安装2.2 环境变量配置3. 报错解决1. 安装cuda 12.1 首先通过nvidia-smi 查看可以安装的CUDA最高版本...
字节和旷视提出HiDiffusion,无需训练,只需要一行代码就可以提高 SD 生成图像的清晰度和生成速度。代码已开源。
字节和旷视提出HiDiffusion,无需训练,只需要一行代码就可以提高 SD 生成图像的清晰度和生成速度。代码已开源。 支持将图像生成的分辨率提高至40964096,同时将图像生成速度提升1.5至6倍。 支持所有 SD 模型同时也支持 SD 模型的下游模型&…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...
