【Rust自学】15.5. Rc<T>:引用计数智能指针与共享所有权
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

15.5.1. 什么是Rc<T>
所有权在大部分情况下都是清晰的。对于一个给定的值,程序员可以准确地推断出哪个变量拥有它。
但是在某些场景中,单个值也可能同时被多个所有者持有,如下图:

在这个图数据结构中,其中的每个节点都有多条边指向它,所以这些节点从概念上讲就是同时属于所以指向它的边。而一个节点只要还有边指向它时就不应该被清理掉。这就是一种多重所有权。
为了支持多重所有权,Rust提供了Rc<T>类型,Rc是Reference counting(引用计数)的简写,这个类型会在实例的内部维护一个用于记录值的引用次数的计数器,从而判断这个值是否仍在使用。如果这个值的引用数量为0,那么这个值就可以被安全地清理掉了,而且不会触发引用实效的问题。
15.5.2. Rc<T>使用场景
当你希望将堆上的一些数据分享给程序的多个部分使用,但是在编译时又无法确定到底是程序的哪个部分最后使用完这些数据时,就可以使用Rc<T>。
相反的,如果我们能在编译时确定程序的哪个部分会最后使用数据,那么只需要让这部分代码成为数据的所有者即可。这样依靠编译时的所有权规则就可以保证程序的正确性了。
需要注意的是,Rc<T>只能用于单线程场景,在以后的文章会研究如何在多线程中使用引用计数。
15.5.3. Rc<T>使用例
在使用前需要注意,Rc<T>不在预导入模块里,想要使用得先手动导入。
Rc下有这么一些基本的函数:
Rc::clone(&a)函数可以增加引用计数Rc::strong_count(&a)可以获得引用计数,而且是强引用的计数- 既然有强引用,那就会有弱引用,也就是
Rc::weak_count函数
用个例子来探究Rc<T>的实际应用:
一共有3个List,分别是a、b和c。其中b和c共享a。其余信息如图:

enum List { Cons(i32, Box<List>), Nil,
} use List::{Cons, Nil};fn main() {// main函数里换行只是为了链表结构更清晰,不是必要let a = Cons(5, Box::new(Cons(10, Box::new(Nil)))); let b = Cons(3, Box::new(a)); let c = Cons(4, Box::new(a));
}
- 首先创建了一个链表
List,其写法在 15.1. 使用Box<T>来指向堆内存上的数据 中就有详细解释,这里不在阐述 - 在
main函数中先把a的结构写出来 - 然后把
b和c的第一层写出来,嵌套的下一层直接写a即可。
逻辑没有问题,运行一下试试:
error[E0382]: use of moved value: `a`--> src/main.rs:17:27|
10 | let a = Cons(5,| - move occurs because `a` has type `List`, which does not implement the `Copy` trait
...
15 | Box::new(a));| - value moved here
16 | let c = Cons(4,
17 | Box::new(a));| ^ value used here after move
报错内容是使用了已移动的值。这是因为在写b时写道了a所以a的所有权就被移到b里了。
这该怎么改呢?
一种办法是修改List的定义,让Cons持有引用而不是所有权,并且要为它指定对应的生命周期参数,但这个生命周期参数会要求List中所有元素的存活时间至少要和List本身一样。借用检查器会阻止我们编译这样的代码:
let a = Cons(10, &Nil);
Nil是一个零大小(zero-sized)的枚举变体,但是在表达式Cons(10, &Nil)或&Nil中,编译器会把它视作一个临时值,这个临时值通常只在当前语句(或更小的作用域)里生效,之后就被自动丢弃。
简单地来说,&Nil是个临时变量,用完就被销毁,生命周期比enum短。临时创建的Nil的变体值会在a取得其引用前就被丢弃。
正确的方法是使用Rc<T>,用引用计数智能指针来让多个所有者共享同一块堆上的数据,并且在所有者都不用后自动释放内存:
enum List { Cons(i32, Rc<List>), Nil,
} use List::{Cons, Nil};
use std::rc::Rc; fn main() { // main函数里换行只是为了链表结构更清晰,不是必要 let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); let b = Cons(3, Rc::clone(&a)); let c = Cons(4, Rc::clone(&a));
}
在声明b和c时,使用Rc::clone并把a的引用&a作为参数传进去,这样b和c就不会获得a的所有权,同时每使用一次Rc::clone就会把智能指针内的引用计数加1。
创建a时使用Rc::new算第一次引用,此时计数器为1;在b和c中各使用了Rc::clone一次,引用计数就会各加1,最终引用计数就是3。a这个智能指针中的数据只有在引用计数为0时才会被清理掉。
其实在Rc<T>上也有clone方法(不是Clone trait的上的clone方法),其源码与Rc::clone完全一样,所以在给b和c赋值时写a.clone()也是可以的。但因为这么写可能会被误解为深拷贝(尤其是对新手来说),而实际它只是增加了引用计数,所以不推荐这么写,更多还是使用Rc::clone。
接下来我们修改一下main函数,打印一些帮助信息,看看当c超出范围时引用计数如何变化:
fn main() {let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));println!("count after creating a = {}", Rc::strong_count(&a));let b = Cons(3, Rc::clone(&a));println!("count after creating b = {}", Rc::strong_count(&a));{let c = Cons(4, Rc::clone(&a));println!("count after creating c = {}", Rc::strong_count(&a));}println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}
这里c会比a和b先走出作用域,所以在c走出作用域后引用计数会减1。
输出:
count after creating a = 1
count after creating b = 2
count after creating c = 3
count after c goes out of scope = 2
在此示例中我们看不到的是,当b和a在main末尾超出范围时,计数为 0,并且Rc<List>被完全清理。
因为Rc<T>实现了Drop trait,所以当Rc<T>离开作用域时引用计数器会自动减1。使用Rc<T>允许单个值拥有多个所有者,并且计数可确保只要任何所有者仍然存在,该值就保持有效。
15.5.4. Rc<T>总结
Rc<T>通过不可变引用,使程序员可以在程序的不同部分之间共享只读的数据。
这里再次强调,Rc<T>引用是不可变的,如果Rc<T>允许程序员持有多个可变引用的话就会违反借用规则(详见 4.4. 引用与借用)——多个指向同一区域的可变引用会导致数据竞争以及数据的不一致。
而在实际开发中肯定会遇到需要数据可变的情况,针对它Rust提供了内部可变性模式和RefCell<T>,程序员可以将其与Rc<T>结合使用来处理此不变性限制。下一篇文章会讲到。
相关文章:
【Rust自学】15.5. Rc<T>:引用计数智能指针与共享所有权
喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 15.5.1. 什么是Rc<T> 所有权在大部分情况下都是清晰的。对于一个给定的值࿰…...
谈谈RTMP|RTSP播放器视频view垂直|水平反转和旋转设计
技术背景 我们在做RTMP|RTSP播放器的时候,有这样的技术诉求,有的摄像头出来的数据是有角度偏差的,比如“装倒了”,或者,图像存在上下或者左右反转,这时候,就需要播放器能做响应的处理ÿ…...
decison tree 决策树
熵 信息增益 信息增益描述的是在分叉过程中获得的熵减,信息增益即熵减。 熵减可以用来决定什么时候停止分叉,当熵减很小的时候你只是在不必要的增加树的深度,并且冒着过拟合的风险 决策树训练(构建)过程 离散值特征处理:One-Hot…...
GO语言 链表(单向链表
链表的前提 GO语言的链表类似于C语言的链表,它通过结构体和结构体指针实现。 结构体 GO语言定义结构体如下 type user struct {name stringage intnext *user } 结构体指针 结构体指针就是指向结构体的指针,我们在链表中会用到结构体指针实现链…...
Java:初识Java
初识Java 一.Java语言概述 1. Java是什么 Java是一种优秀的程序设计语言,它具有令人赏心悦目的语法和易于理解的语义。 不仅如此,Java还是一个有一系列计算机软件和规范形成的技术体系,这个技术体系提供了完整的用于软件开发和跨平台部署的…...
Spring WebSocket 与 STOMP 协议结合实现私聊私信功能
目录 后端pom.xmlConfig配置类Controller类DTO 前端安装相关依赖websocketService.js接口javascripthtmlCSS 效果展示简单测试连接: 报错解决方法1、vue3 使用SockJS报错 ReferenceError: global is not defined 功能补充拓展1. 安全性和身份验证2. 异常处理3. 消息…...
从0到1:C++ 开启游戏开发奇幻之旅(一)
目录 为什么选择 C 进行游戏开发 性能卓越 内存管理精细 跨平台兼容性强 搭建 C 游戏开发环境 集成开发环境(IDE) Visual Studio CLion 图形库 SDL(Simple DirectMedia Layer) SFML(Simple and Fast Multim…...
基于Flask的哔哩哔哩综合指数UP榜单数据分析系统的设计与实现
【Flask】基于Flask的哔哩哔哩综合指数UP榜单数据分析系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统旨在通过大数据分析和数据挖掘技术,结合Flask轻量级We…...
在php中怎么打开OpenSSL
(点击即可进入聊天助手) 背景 在使用php做一些项目时,有用到用户邮箱注册等,需要开启openssl的能力 在php系统中openssl默认是关闭状态的,在一些低版本php系统中,有的甚至需要在服务器终端后台,手动安装 要打开OpenSSL扩展,需要进行以下步骤 …...
oracle 分区表介绍
oracle 分区表介绍 Oracle 分区表是一个非常强大的数据库功能,可以将一个大的表分割成多个更小、更易管理的块(分区)。这种分区结构在处理大规模数据时非常有用,因为它能改善性能、简化维护和管理,并支持高效的数据存取…...
wxwidgets直接获取系统图标,效果类似QFileIconProvider
目前只做了windows版本,用法类似QFileIconProvider // 头文件 #ifndef WXFILEICONPROVIDER_H #define WXFILEICONPROVIDER_H#include <wx/wx.h> #include <wx/icon.h> #include <wx/image.h> #include <wx/bmpcbox.h> // Include for wxB…...
Arduino大师练成手册 -- 控制 PN532 NFC 模块
要在 Arduino 上控制 PN532 NFC 模块,你可以按照以下步骤进行: 硬件连接 VCC:连接到 Arduino 的 3.3V 引脚。 GND:连接到 Arduino 的 GND 引脚。 SDA:连接到 Arduino 的 SDA 引脚(通常是 A4)…...
解决日志中 `NOT NULL constraint failed` 异常的完整指南
在开发和运维过程中,日志是我们排查问题的重要工具。然而,当日志中出现类似 NOT NULL constraint failed 的异常时,往往意味着数据库约束与代码逻辑不匹配。本文将详细分析此类问题的原因,并提供完整的解决方案。 © ivwdcwso (ID: u012172506) 问题描述 在同步 AWS …...
C动态库的生成与在Python和QT中的调用方法
目录 一、动态库生成 1)C语言生成动态库 2)c类生成动态库 二、动态库调用 1)Python调用DLL 2)QT调用DLL 三、存在的一些问题 1)python调用封装了类的DLL可能调用不成功 2)DLL格式不匹配的问题 四、…...
UE求职Demo开发日志#7 强化属性完善
1 实现思路设计 定义一个结构体记录技能树一个单元的信息,命名为FStrengthenCellInfo,一个TArray记录技能树整体信息,需要以下信息: 1.TArray前置技能index 2.FString 描述文本 3.TArray<FMyItemInfo>激活需要的物品ID和…...
Day35:字符串的大小写转换
在 Python 中,字符串的大小写转换是一个常见的操作,它可以帮助我们快速地将字符串中的字母从大写转换为小写,或者从小写转换为大写。Python 提供了多种方法来进行字符串大小写的转换,包括 upper()、lower()、capitalize()、title(…...
喜报丨迪捷软件入选2025年浙江省“重点省专”
根据《浙江省经济和信息化厅 浙江省财政厅关于进一步支持专精特新中小企业高质量发展的通知》(浙经信企业〔2024〕232号)有关要求,经企业自主申报、地方推荐、材料初审以及专家评审等程序,浙江省经济和信息化厅发布了2025年浙江省…...
深度剖析 PyTorch框架:从基础概念到高级应用的深度学习之旅!
目录 一、引言 二、PyTorch 简介 (一)诞生背景与发展历程 (二)核心特点 三、PyTorch 基础概念 (一)张量(Tensor):数据的基石 (二)自动微分&…...
基于C++的DPU医疗领域编程初探
一、大型医院数据处理困境与 DPU 的崛起 在数字化浪潮的席卷下,医疗行业正经历着深刻变革,大型医院作为医疗服务的核心枢纽,积累了海量的数据,涵盖患者的基本信息、诊断记录、检验报告、影像资料等多个维度。这些数据不仅规模庞大,而且增长速度迅猛,传统的中央处理器(C…...
Linux 执行 fdisk -l 出现 GPT PMBR 大小不符 解决方法
目录 前言1. 问题所示2. 原理分析3. 解决方法前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 1. 问题所示 执行fdisk -l的时候出现如下提示: [root@VMS-Centos-test1 ~]# fdisk -l GPT PMBR 大小不符(419430399 != 4294967295),将用写入予以更正…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
