21.智能指针(上)
目录
- 一、概念
- 二、Box\<T\>
- 2.1 概念与应用场景
- 2.2 简单应用
- 2.3 递归类型的创建
- 三、通过Deref trait将智能指针当作常规引用处理
- 3.1 常规引用
- 3.2 像引用一样使用Box\<T\>
- 3.3 自定义智能指针
- 3.4 函数和方法的隐式解引用强制转换
- 3.5 解引用强制转换与可变性交互
- 四、使用Drop Trait清理代码
- 4.1 自动运行
- 4.2 手动丢弃
一、概念
- 在Rust中,引用是只是借用数据的指针,智能指针拥有它们所指向的数据的所有权;
- 智能指针通常使用结构体实现;
- 智能指针实现了
Deref trait
,值可以被当作引用对待; - 智能指针实现了
Drop trait
,当值离开作用域时,其所指向的堆数据也去被清除; - 常用的智能指针见下表
指针 | 功能说明 |
---|---|
Box<T> | 用于在堆上分配值,允许在编译时执行不可变或可变借用检查 |
Rc<T> | 一个引用计数类型,相同数据可以有多个所有者,仅允许在编译时执行不可变借用检查 |
RefCell<T> | 允许在运行时执行不可变或可变借用检查;可以在即使RefCell<T> 自身是不可变的情况下修改其内部的值 |
Ref<T> 和 RefMut<T> | 通过RefCell<T> 访问 |
- 内部可变性模式:在不可变值内部改变值;
二、Box<T>
2.1 概念与应用场景
- box是最简单最直接的智能指针,其类型是
box<T>
; - box主要应用于以下场景:
- 编译时未知大小的类型,但使用时却需要知道它的确切大小;
- 有大量数据且希望在确保数据不被拷贝的情况下转移所有权;
- 只关心值的类型是否实现了特定 trait;
2.2 简单应用
fn main(){let b = Box::new(5);println!("b = {}", b);
}
- 变量b指向了分配在堆上的值为5的Box;
- b拥有这块内存的所有权,离开作用域后堆内存被自动释放;
2.3 递归类型的创建
- Rust需要在编译时知道类型占用的空间大小;
- box的已知大小,让其可以在循环类型定义中插入box,就可以创建递归类型;
enum List{Cons(i32, Box<List>),Nil,
}use crate::List::{Cons, Nil};
fn main() {let list = Cons(1,Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
- Cons成员将需要一个
i32
类型的空间大小以及box指针数据的空间; - Nil成员不存储值,因此它比Cons成员需要更少的空间;
- 看起来像这样
- 如果不用Box定义递归,写成下面这样
enum List{Cons(i32, List),Nil,
}use crate::List::{Cons, Nil};
fn main() {let list = Cons(1, Cons(2, Cons(3, Nil)));
}
- 则编译报错,表明类型占用的空间无限大;
- 其空间排布类型于
三、通过Deref trait将智能指针当作常规引用处理
- 实现Deref trait可以让使用者重载解引用运算符(dereference operator)
*
; - 这种方式实现Deref trait的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针;
3.1 常规引用
- 常规引用是一种指针类型;
fn main() {let x = 5;let y = &x;assert_eq!(5, x);assert_eq!(5, *y);
}
- y等于x的引用,使用
*y
访问x的值;
3.2 像引用一样使用Box<T>
let x = 5;let y = Box::new(x);assert_eq!(5, x);assert_eq!(5, *y);
- 代码可正常运行不报错;
3.3 自定义智能指针
use std::ops::Deref;struct MyBox<T>(T);impl<T> MyBox<T> {fn new(x: T) -> MyBox<T> {MyBox(x)}
}impl<T> Deref for MyBox<T>{type Target = T;fn deref(&self) -> &T {&self.0}
}fn main() {let x = 5;let y = MyBox::new(x);assert_eq!(5, x);assert_eq!(5, *y);
}
- MyBox<T> 被定义为包含一个元素的元组结构体;
- new函数获取一个T类型的参数并返回一个存入传入值的实例;
- 为MyBox实现
Deref
trait才能启动*
运算符的解引用功能; impl<T> Deref for MyBox<T>
中的type Target = T 定义了此trait的关联类型;deref
方法返回了一个值的引用,如果直接返回值,则值的选择权将被移出self;- 当使用
*y
时,底层运行了代码*(y.deref())
;
3.4 函数和方法的隐式解引用强制转换
- 解引用强制转换只能工作在实现了
Deref
trait 的类型上; - 解引用强制转换是将一种类型隐式转换为另外一种类型的引用;
- 前一种类型实现了
Deref
trait,并且其关联类型是后一种类型;
例如,解引用强制转换可以将 &String 转换为 &str,因为类型 String 实现了 Deref trait 并且其关联类型是 str;
#[stable(feature = "rust1", since = "1.0.0")]
impl ops::Deref for String {type Target = str;#[inline]fn deref(&self) -> &str {unsafe { str::from_utf8_unchecked(&self.vec) }}
}
- 将特定类型的值的引用传递给函数且与函数定义的参数类型不匹配时,会发生解引用强制转换;
- 此时有一系列的
deref
方法被调用,将我们提供的参数类型转换成函数或方法需要的参数类型; - 解引用强制转换功能可以让开发者编写函数和方法调用时无需增加过多显式使用
&和*
引用和解引用。 - 解引用强制转换功能也使得开发者可以编写更多同时作用于引用或智能指针的代码;
use std::ops::Deref;struct MyBox<T>(T);impl<T> MyBox<T> {fn new(x: T) -> MyBox<T> {MyBox(x)}
}impl<T> Deref for MyBox<T>{type Target = T;fn deref(&self) -> &T {&self.0}
}fn hello(name: &str){println!("Hello, {}", name);
}fn main() {let m = MyBox::new(String::from("Rust"));hello(&m);
}
- main函数中的m为MyBox<String>值的引用;
- MyBox<T>上实现了
Deref
trait,Rust可以通过deref调用将&MyBox<String>
变为&String
; - 再次调用deref将&String 变为 &str;
- 如果没有实现解引用强制转换,为了使用
&MyBox<String>
类型的值调用hello函数,应该这样写
fn main() {let m = MyBox::new(String::from("Rust"));hello(&(*m)[..]);
}
- (*m) 将 MyBox<String> 解引用为 String;
- &和[…] 获取了整个 String 的字符串 slice 来匹配 hello 函数的参数;
- 没有解引用强制转换所有这些符号混在一起将更难以读写和理解;
- Rust的解引用强制转换发生在编译,因此在运行时没有损耗!
3.5 解引用强制转换与可变性交互
- 类似于使用 Deref trait 重载不可变引用的
*
运算符,Rust提供了DerefMuttrait
用于重载可变引用的*
运算符; - Rust 在发现类型和 trait 的实现满足以下三种情况时会进行解引用强制转换;
- 当 T: Deref<Target=U> :从 &T 到 &U
如果有一个&T,而T实现了返回U类型的Deref,则可以直接得到&U - 当 T: DerefMut<Target=U> :从 &mut T 到 &mut U
对于可变引用有着与第一种相同的行为; - 当 T: Deref<Target=U> :从 &mut T 到 &U
Rust也会将可变引用强转为不可变引用,但是反之是不可能的,因为不可变引用永远也不能强转为可变引用;
- 当 T: Deref<Target=U> :从 &T 到 &U
四、使用Drop Trait清理代码
4.1 自动运行
- 通过实现
Drop
trait指定变量离开作用域时被执行的代码; - 可以理解为析构函数;
struct CustomSmartPointer {data: String,
}impl Drop for CustomSmartPointer {fn drop(&mut self) {println!("Dropping CustomSmartPointer with data `{}`!", self.data);}
}fn main() {{let c = CustomSmartPointer { data: String::from("stuff c") };}let d = CustomSmartPointer { data: String::from("stuff d") };let e = CustomSmartPointer { data: String::from("stuff e") };println!("CustomSmartPointers created.");
}
- main函数中离开最内层的大括号后,变量c首先离开作用域,自动调用
drop
方法; - 然后打印CustomSmartPointers created.
- 变量d、e最后离开作用域,再自动调用对应的
drop
方法; - d、e的输出结果显示,以先进后出的方式调用drop方法;
4.2 手动丢弃
- 不能显式的调用
drop
方法; - 如果要在作用域结构之前强制释放变量,使用
drop(x)
实现;
fn main() {{let c = CustomSmartPointer { data: String::from("stuff c") };}let d = CustomSmartPointer { data: String::from("stuff d") };drop(d);let e = CustomSmartPointer { data: String::from("stuff e") };println!("CustomSmartPointers created.");
}
运行代码,可以发现d
被提前析构;
相关文章:

21.智能指针(上)
目录 一、概念二、Box\<T\>2.1 概念与应用场景2.2 简单应用2.3 递归类型的创建 三、通过Deref trait将智能指针当作常规引用处理3.1 常规引用3.2 像引用一样使用Box\<T\>3.3 自定义智能指针3.4 函数和方法的隐式解引用强制转换3.5 解引用强制转换与可变性交互 四、…...

Jenkins+gitee流水线部署springboot项目
目录 前言 一、软件版本/仓库 二、准备工作 2.1 安装jdk 11 2.2 安装maven3.9.7 2.3 安装docker 2.4 docker部署jenkins容器 三、jenkins入门使用 3.1 新手入门 3.2 jenkins设置环境变量JDK、MAVEN、全局变量 3.2.1 jenkins页面 3.2.2 jenkins容器内部终端 3.2.3 全…...
python--os.walk()函数使用(超详细)
在Python 3.7中,os.walk()函数的用法与早期版本(包括Python 3.4及之后)保持一致。os.walk()是一个用于遍历目录树的生成器函数,它生成给定目录中的文件名。这个函数没有直接的参数(除了你要遍历的目录路径,…...
基础名词概念
了解以下基础名词概念/定义: IP地址、子网掩码、网关、DNS、DHCP、MAC地址、网络拓扑、路由器、交换机、VPN、端口、TCP、UDP、HTTP、HTTPS、OSI模型、ARP、NAT、VLAN、FTP、SMTP、IMAP、SSL、ICMP、链路聚合、TRUNK、直连路由、静态路由、动态路由、IPV6 端口&am…...

ArkTS开发系列之Web组件的学习(2.9)
上篇回顾:ArkTS开发系列之事件(2.8.2手势事件) 本篇内容: ArkTS开发系列之Web组件的学习(2.9) 一、知识储备 Web组件就是用来展示网页的一个组件。具有页面加载、页面交互以及页面调试功能 1. 加载网络…...
postman接口工具的详细使用教程
Postman 是一种功能强大的 API 测试工具,可以帮助开发人员和测试人员轻松地发送 HTTP 请求并分析响应。以下是对 Postman 接口测试工具的详细介绍: 安装与设置 安装步骤 访问 Postman 官网,点击右上角的“Download”按钮。 选择你的操作系统…...
C语言经典例题-17
1.最小公倍数 正整数A和正整数B的最小公倍数是指能被A和B整除的最小的正整数,设计一个算法,求输入A和B的最小公倍数。 输入描述:输入两个正整数A和B。 输出描述:输出A和B的最小公倍数。 输入:5 7 输出:…...
鸿蒙学习(-)
.ets文件结构 //页面入口 Entry //组件 Component struct test{//页面结构build(){//容器 **一个页面只能有一个根容器,父容器要有大小设置**}1、Column 组件 沿垂直方向布局的组件,可以包含子组件 接口 Column({space}) space的参数为string | numbe…...

【TB作品】MSP430G2553,单片机,口袋板, 烘箱温度控制器
题3 烘箱温度控制器 设计一个基于MSP430的温度控制器,满足如下技术指标: (1)1KW 电炉加热,最度温度为110℃ (2)恒温箱温度可设定,温度控制误差≦2℃ (3)实时显…...

PCM、WAV,立体声,单声道,正弦波等音频素材
1)PCM、WAV音频素材,分享给将要学习或者正在学习audio开发的同学。 2)内容属于原创,若转载,请说明出处。 3)提供相关问题有偿答疑和支持。 常用的Audio PCM WAV不同采样率,不同采样深度&#…...
基于深度学习的图像去雾
基于深度学习的图像去雾 图像去雾是指从有雾的图像中恢复清晰图像的过程。传统的图像去雾方法(如暗原色先验、图像分层法等)在某些情况下表现良好,但在复杂场景下效果有限。深度学习方法利用大量的数据和强大的模型能力,在图像去…...
中国电子学会青少年编程等级考试真题下载
全国青少年软件编程等级考试真题下载,有答案解析 1. 图形化Scratch一级下载 链接:https://pan.baidu.com/s/1C9DR9-hT1RUY3417Yc8RZQ?pwdg8ac 提取码:g8ac 2.图形化Scratch二级下载 链接:https://pan.baidu.com/s/1HI7GaI4ii…...

PostMan动态设置全局变量
1. 前言 在开发过程中调试接口,一般都会使用PostMan。 其中有几个变量可能是好几个接口共用的,就会出现频繁手动复制(ctrlc)、粘贴(ctrlv)的情况。 这个过程得非常留意,生怕复制错了,或删减了某些东西,导致接口报错。…...

ACL 2023事件相关(事件抽取、事件关系抽取、事件预测等)论文汇总
ACL 2023事件抽取相关(事件抽取、事件关系抽取、事件预测等)论文汇总,后续会更新全部的论文讲解。 Event Extraction Code4Struct: Code Generation for Few-Shot Event Structure Prediction 数据集:ACE 2005 动机:与自然语言相比…...

力扣:59. 螺旋矩阵 II(Java,模拟)
目录 题目描述示例 1:代码实现 题目描述 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1: 输入:n 3 输出:[[1,2,3],[8,9,4],[7,6,5…...

记录SpringBoot启动报错解决
记录SpringBoot启动报错解决 报错现场 Failed to configure a DataSource: url attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class Action: Consider the following:If you want an embedde…...
微软代码页标识符 (Code Page Identifiers)
代码页标识符 (Code Page Identifiers) 双语对照 Identifiere标识符.NET Name.NET 名称Additional information其他信息037IBM037IBM EBCDIC US-CanadaIBM EBCDIC US-Canada437IBM437OEM United StatesOEM 美国500IBM500IBM EBCDIC InternationalIBM EBCDIC 国际字符集708ASMO…...
刷题——二叉树的后续遍历
方法一:双指针法 void postorder(TreeNode* root, vector<int>&res){if(root NULL) return;postorder(root->left,res);postorder(root->right,res);res.push_back(root->val);}vector<int> postorderTraversal(TreeNode* root) {// wri…...

用友U8 Cloud smartweb2.showRPCLoadingTip.d XXE漏洞复现
0x01 产品简介 用友U8 Cloud 提供企业级云ERP整体解决方案,全面支持多组织业务协同,实现企业互联网资源连接。 U8 Cloud 亦是亚太地区成长型企业最广泛采用的云解决方案。 0x02 漏洞概述 用友U8 Cloud smartweb2.showRPCLoadingTip.d 接口处存在XML实体,攻击者可通过该漏…...
React中的事件绑定的四种方式
1.在构造函数中绑定事件 constructor(props) {super(props);this.handleClick this.handleClick.bind(this);}2.在调用时显式绑定 <button onClick{this.handleClick.bind(this)}>Click me</button>3.使用箭头函数 handleClick () > {console.log(Button cli…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...