【Rust】16. 智能指针

news2024/11/18 22:31:00
  • 智能指针(smart pointers)是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和功能
  • 引用是一类只借用数据的指针;相反,在大部分情况下,智能指针拥有他们指向的数据

在这里插入图片描述

16.1 Box<T>:指向堆上的数据

  • box 允许将一个值放在堆上而不是栈上,box 是一个本身留在栈上但指向堆数据的指针
  • box 的作用:提供固定大小、提供堆分配、间接存储

在这里插入图片描述

16.1.1 使用 Box<T> 在堆上储存数据

  • box:在离开作用域时,将被释放,释放过程作用于 box 本身(位于栈上)和它所指向的数据(位于堆上)

在这里插入图片描述

16.1.2 Box 的应用:创建递归类型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

16.1.3 计算非递归类型的大小

  • enum:实际上只会使用其中的一个成员,所以枚举值所需的空间等于储存其最大成员的空间大小

在这里插入图片描述

16.1.4 使用 Box<T> 给递归类型一个已知的大小

  • Box<T> 是一个指针,我们总是知道它需要多少空间:指针的大小并不会根据其指向的数据量而改变
  • 核心思想:建议中的 “indirection” 意味着不同于直接储存一个值,应该间接的储存一个指向值的指针!
  • 通过使用 box,打破了这无限递归的连锁,这样编译器就能够计算出储存 List 值需要的大小了

在这里插入图片描述
在这里插入图片描述

16.2 通过 Deref trait 将智能指针当作常规引用处理

  • 实现 Deref trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针

在这里插入图片描述

16.2.1 解引用 *:追踪指针的值

  • 解引用运算符 * 可用于追踪引用 & 所指向的值

在这里插入图片描述

16.2.2 像引用一样使用 Box<T>

  • Box<T> 可用于生成拷贝一个值的引用(注意是引用,而不是值)的实例(而不是指向该值的引用,因为生成的是 Box 引用,不是常规引用,二者不可比

在这里插入图片描述

16.2.3 自定义智能指针(一):引入

在这里插入图片描述

16.2.4 自定义智能指针(二):实现 Deref trait 将某类型像引用一样处理

  • 对于实现 Deref trait 的数据类型,要求实现 deref 方法,该方法借用 self 并返回一个内部数据的引用、并可用于后续的解引用
  • 没有 Deref trait 的话,编译器只会解引用 & 引用类型
  • *y 的底层运行为 *(y.deref()):Rust 将 * 运算符替换为先调用 deref 方法再进行普通解引用的操作

在这里插入图片描述

16.2.5 函数和方法的隐式 Deref 强制转换

  • Deref 强制转换(deref coercions)将实现了 Deref trait 的类型的引用转换为另一种类型的引用
  • 当这种特定类型的引用作为实参传递给和形参类型不同的函数或方法时将自动进行,这时会有一系列的 deref 方法被调用,把我们提供的类型转换成了参数所需的类型

在这里插入图片描述

16.2.6 Deref 强制转换如何与可变性交互

  • Deref trait 重载不可变引用* 运算符;DerefMut trait 用于重载可变引用的 * 运算符
  • Rust 在发现类型和 trait 实现满足三种情况时会进行 Deref 强制转换,如下所示:
    1. T: Deref<Target=U> 时从 &T&U:如果有一个 &T,而 T 实现了返回 U 类型的 Deref,则可以直接得到 &U
    1. T: DerefMut<Target=U> 时从 &mut T&mut U:与 1 类似(同上)
    1. T: Deref<Target=U> 时从 &mut T&URust 也会将可变引用强转为不可变引用,但与之相反的,不可变引用永远也不能强转为可变引用!

在这里插入图片描述

16.3 Drop Trait:运行清理代码

16.3.1 Drop Trait 的基本概念

在这里插入图片描述
在这里插入图片描述

16.3.2 std::mem::drop:提前丢弃值

  • std::mem::drop 可显式调用来丢弃值,该方法已经存在于 prelude 中
  • 所有权系统确保引用总是有效的,也会确保 drop 只会在值不再被使用时被调用一次

在这里插入图片描述
在这里插入图片描述

16.4 Rc<T>:引用计数智能指针

  • 引用计数(reference counting):意味着记录一个值引用的数量来知晓这个值是否仍在被使用,如果某个值有零个引用,就代表没有任何有效引用并可以被清理
  • 如果确实知道哪部分是最后一个结束使用的话,就可以令其成为数据的所有者,正常的所有权规则就可以在编译时生效
  • 为了启用多所有权需要显式地使用 Rust 类型 Rc<T>,注意 Rc<T> 只能用于单线程场景

在这里插入图片描述

16.4.1 使用 Rc<T> 共享数据

  • Rc<T> 可通过克隆的方式 Rc::clone 来增加引用计数,直到有零个引用之前其数据都不会被清理
  • Rc::clone 的实现并不像大部分类型的 clone 实现那样对所有数据进行深拷贝,只会增加引用计数,这并不会花费多少时间,可以明显的区别深拷贝类的克隆和增加引用计数类的克隆(在下面的示例中也可以调用 a.clone()

在这里插入图片描述
在这里插入图片描述

16.4.2 克隆 Rc 会增加引用计数

  • Rc::strong_count:返回引用计数的值

在这里插入图片描述

16.5 RefCell<T> 和内部可变性模式

  • 内部可变性(Interior mutability)是 Rust 中的一个设计模式,它允许即使在有不可变引用时也可以改变数据,这通常是借用规则所不允许的。为了改变数据,该模式在数据结构中使用 unsafe 代码来模糊 Rust 通常的可变性和借用规则

在这里插入图片描述

16.5.1 RefCell<T>:在运行时检查借用规则

  • 对于引用和 Box<T>:借用规则的不可变性作用于编译时;如果违反这些规则会得到一个编译错误
  • 对于 RefCell<T>:这些不可变性作用于运行时;而对于 RefCell<T>,如果违反这些规则程序会 panic 并退出

在这里插入图片描述

16.5.2 内部可变性:不可变值的可变借用

  • 借用规则推论:对于一个不可变值,不能可变的借用它

在这里插入图片描述

16.5.3 内部可变性的示例:mock 对象

  • std::cell::RefCell:内部可变性相关模块
  • 在下面的示例中,对于要修改的结构体字段 sent_messages 字段的类型是 RefCell<Vec<String>> 而不是 Vec<String>;在 new 函数中新建了一个 RefCell<Vec<String>> 实例替代空 vector
  • RefCell::borrow_mut 方法来获取 RefCell 中值的可变引用;RefCell::borrow 方法来获取 RefCell 中值的不可变引用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

16.5.4 RefCell<T>:在运行时记录借用

  • 不可变引用:一般使用 & 语法;对于 RefCell<T> 使用 borrow 方法,返回一个 Ref<T> 类型的智能指针,且实现了 Deref trait
  • 可变引用:一般使用 &mut 语法;对于 RefCell<T> 使用 borrow_mut 方法,返回一个 RefMut<T> 类型的智能指针,且实现了 Deref trait
  • RefCell<T> 记录当前有多少个活动的 Ref<T>RefMut<T> 智能指针
  • 每次调用 borrowRefCell<T> 将活动的不可变借用计数加一;当 Ref<T> 值离开作用域时,不可变借用计数减一
  • 像编译时借用规则一样,RefCell<T> 在任何时候只允许有多个不可变借用或一个可变借用
  • 如果违反借用规则,相比引用时的编译时错误,RefCell<T> 的实现会在运行时出现 panic

在这里插入图片描述
在这里插入图片描述

16.5.5 结合 Rc<T> 和 RefCell<T> 来拥有多个可变数据所有者

在这里插入图片描述
在这里插入图片描述

16.6 引用循环与内存泄漏

在这里插入图片描述

16.6.1 制造引用循环

  • 创建引用循环并不容易,但也不是不可能:如果你有包含 Rc<T>RefCell<T> 值或类似的嵌套结合了内部可变性和引用计数的类型,请小心检查有没有形成一个引用循环
  • 另一个解决方案是重新组织数据结构,使得一部分引用拥有所有权而另一部分没有(换句话说,循环将由一些拥有所有权的关系和一些无所有权的关系组成,只有所有权关系才能影响值是否可以被丢弃)
  • 代码解析:*link.borrow_mut() = Rc::clone(&b); 表示将取出的变量 link 引用通过 borrow_mut() 方法来获得其可变引用,再用 * 来解引用指针,解引用后将这个可变引用重新指向 b 的克隆引用(*link.borrow_mut() 应该等价于 *(link.borrow_mut())

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

16.6.2 避免引用循环:将 Rc<T> 变为 Weak<T>

  • 创建弱引用(weak reference):调用 Rc::downgrade 并传递 Rc<T> 实例的引用来创建其值的弱引用,并会得到 Weak<T> 类型的智能指针,会将 weak_count 加 1。创建弱引用的例子:*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
  • 弱引用的 weak_count:用来记录存在多少个 Weak<T> 引用,与 strong_count 的区别在于,weak_count 无需计数为 0 就能使 Rc<T> 实例被清理
  • 使用 upgrade 方法用于判断指向的值是否被丢弃:因为 Weak<T> 引用的值可能已经被丢弃了,为了使用 Weak<T> 所指向的值,我们必须确保其值仍然有效,为此可以调用 Weak<T> 实例的 upgrade 方法,这会返回 Option<Rc<T>>
  • 强、若引用的区别:强引用代表如何共享 Rc<T> 实例的所有权(引用并拥有),但弱引用并不属于所有权关系(仅引用,但不拥有)!
  • 弱引用不会造成引用循环,因为任何弱引用的循环会在其相关的强引用计数为 0 时被打断

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

16.7 小结

在这里插入图片描述

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

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

相关文章

1610_PC汇编语言_整形的表达

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 前面对于汇编有了一个基本的了解&#xff0c;这一章节主要是看一下汇编视角下的数据表达以及计算方式。 1. 整形会有有符号和无符号两种&#xff0c;一般都是用最高…

快上车,程序狗好用的奇淫技巧

文章目录前言&#x1f34a;缘由⏲️本文阅读时长&#x1f3af;主要目标正文&#x1f9d9;‍♂️1.魔术橡皮擦&#x1f415;2.狗屁不通文章生成器&#x1f95e;3.easypdf&#x1f97d;4.Md2All&#x1f32e;5.CSDN开发助手&#x1f468;‍&#x1f4bb;6.猿如意&#x1f9e9;7.P…

Three.js 初阶基础篇(二)

系列文章目录 我今天又来了更新了&#xff01;&#xff01;&#xff01;今天主要还是回顾一下昨天的一内容&#xff0c;在昨天的基础上又重新梳理了一下&#xff0c;创建动态3D正方体的流程&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 文章目录…

VMware虚拟机安装Linux教程

一、centos7下载 1、centos7的下载 官网下载地址: https://www.centos.org/download/ 2、点击x86_64 二、虚拟机下载与安装 1、VMware虚拟机下载 官网地址: https://www.vmware.com/cn/products/workstation-player.html 打开下载好的.exe文件 安装位置默认在 C 盘下…

【JavaSE】运算符

运算符BIT-3 运算符1. 什么是运算符2. 算数运算符2.1 基本四则运算符2.2 增量运算符2.3 自增/自减运算符3. 关系运算符4. 逻辑运算符&#xff08;重点&#xff09;4.1 逻辑与&&4.2 逻辑或||4.3 逻辑非&#xff01;4.4 短路求值5. 位运算符5.1 按位与&5.2 按位或|5.…

【借助pf4j实现基于spring-boot的插件化开发

借助pf4j实现基于spring-boot的插件化开发背景场景项目地址&结构Demo运行背景 Jenkins、SonarQube等Java实现的Web应用都有插件体系&#xff0c;一般来说都是先从插件市场下载一个插件&#xff0c;然后系统要求重启(某些功能可以不用重启)&#xff0c;插件功能就能在页面上…

SRM-供应商管理系统搭建指南

1、简介1.1、案例简介本文将介绍&#xff0c;如何搭建SRM-供应商管理。1.2、应用场景供应商可注册、提交、修改自己的基本信息及工商信息&#xff0c;上传资质档案、管理产品及样品信息&#xff1b;企业对供应商是否成为合格供应商或淘汰供应商进行准入流程的审批。2、设置方法…

软件设计(四)--MTBF、MTTF、MTTR

软件设计&#xff08;三&#xff09;https://blog.csdn.net/ke1ying/article/details/128808681 31、结构化开发中&#xff0c;过程设计 主要包含对数据结构和算法的设计。 体系结构设计&#xff1a;定义软件的主要结构元素及其关系。 数据设计&#xff1a;基于实体联系图&a…

高压放大器在多波长干涉相位同步解调方法研究中的应用

实验名称&#xff1a;基于激光相位分立调制的多波长干涉相位同步解调方法研究 研究方向&#xff1a;激光测量 测试目的&#xff1a; 在长度测量中&#xff0c;绝对距离测量&#xff08;ADM&#xff09;可实现高精度、大范围和瞬时距离测量&#xff0c;与相对位移测量&#xff0…

Dynamsoft Barcode Reader 9.6.1 for NET Crack

Dynamsoft Barcode Reader以每分钟 500 的速度扫描条码 Dynamsoft Barcode Reader具有灵活 API 的强大条码扫描器 SDK Dynamsoft Barcode Reader无论它是模糊的、扭曲的、黑暗的、遥远的还是移动的&#xff0c;我们都可以扫描它。快速地。 业界最快扫描解码&#xff0c;适用于…

一次线上超时问题,看Ribbon 超时机制

一、线上问题 项目上线后&#xff0c;一段时间内运行都没有问题&#xff0c;突然运营人员说&#xff0c;某个接口一直失败。遂查看线上日志&#xff1a; spring-cloud-openfeign 通过服务名调用的&#xff0c;服务发现没有找到可用服务实例&#xff1f;这是第一反应&#xff0…

如何添加PDF书签?学会这三种方法

随着PDF文档和书籍已经成为我们工作和生活中不可或缺的一部分&#xff0c;许多PDF书签制作器和编辑器应运而生。了解使用 Windows、Mac 兼容或在线软件免费将书签添加到 PDF的多种方法。 什么是 PDF 书签&#xff1f; PDF 文件中的书签与印刷书籍中的纸质或织物书签的工作方式…

1609_PC汇编语言_汇编简介以及first例程后续

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 看了一下&#xff0c;当前看的这个章节还剩下几页没有看完。看完之后&#xff0c;做一个简单的整理。 在上一篇笔记中我提到了关于DJGPP的疑惑&#xff0c;也自己找…

RocketMQ源码-NameServer架构设计及启动流程

本文我们来分析NameServer相关代码&#xff0c;在正式分析源码前&#xff0c;我们先来回忆下NameServer的功能&#xff1a; NameServer是一个非常简单的Topic路由注册中心&#xff0c;其角色类似Dubbo中的zookeeper&#xff0c;支持Broker的动态注册与发现。主要包括两个功能&…

【每日阅读】前端进阶知识点1

如何更改网页中语言属性值 声明当前语言类 html标签更改属性值 lang属性中不区分大小写 en-us en-US 一致 具体可使用 window,document.querySelector(“html”)?.setAttribute(“lang”,newValue); qs库 qs是一个流行的查询参数序列化和解析库。可以将一个普通的object序列…

09_FreeRTOS任务调度器

目录 开启任务调度器vTaskStartScheduler函数 xPortStartScheduler开启任务调度器函数 启动第一个任务 prvStartFirstTask开启第一个任务函数 vPortSVCHandler SVC中断服务函数 出栈/压栈汇编指令详解 开启任务调度器vTaskStartScheduler函数 作用:用于启动任务调度器,…

LaoCat带你认识容器与镜像(实践篇一上)

实践篇主要以各容器的挂载和附加命令为主。 本章内容 Dockerfile基础命令详解。 本文实操全部基于Ubuntu 20.04 宿主机 > linux服务器本身 Docker > 20.10.22 从该章开始&#xff0c;我们就进入了Docker实践篇系列了&#xff0c;主要介绍Docker相关附加命令&#xff0c;…

Python中的字符串多样的操作方法

文章目录1.字符串中大小写字母的转变2.字符串的左右中对齐3.字符串查找的方法4.字符串的替换5. 字符串的判断6.字符串的截取7.字符串的拆分8.字符串的拼接9.格式化字符串10.格式化字符串的语法10.12.1 对齐选项&#xff08;[align]&#xff09;10.2 填充选项&#xff08;[fill]…

切入点(pointcut)的申明规则

Spring AOP 用户可能会经常使用 execution切入点指示符。执行表达式的格式如下&#xff1a;execution&#xff08;modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern&#xff08;param-pattern&#xff09; throws-pattern?&#xff09;ret-type-p…

HandlerInterceptorAdapter拦截器多个拦截器时执行的顺序

前提&#xff1a;拦截器加载顺序是 1 2 3.Configuration public class InterceptorConfig implements WebMvcConfigurer {Autowiredprivate Test1neInterceptor test1neInterceptor;Autowiredprivate Test2neInterceptor test2neInterceptor;Overridepublic void addIntercepto…