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实现
Dereftrait才能启动*运算符的解引用功能; impl<T> Deref for MyBox<T>中的type Target = T 定义了此trait的关联类型;deref方法返回了一个值的引用,如果直接返回值,则值的选择权将被移出self;- 当使用
*y时,底层运行了代码*(y.deref());
3.4 函数和方法的隐式解引用强制转换
- 解引用强制转换只能工作在实现了
Dereftrait 的类型上; - 解引用强制转换是将一种类型隐式转换为另外一种类型的引用;
- 前一种类型实现了
Dereftrait,并且其关联类型是后一种类型;
例如,解引用强制转换可以将 &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>上实现了
Dereftrait,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 自动运行
- 通过实现
Droptrait指定变量离开作用域时被执行的代码; - 可以理解为析构函数;
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…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...
