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…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
