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…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...
如何通过git命令查看项目连接的仓库地址?
要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法: 1. 查看所有远程仓库地址 使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL: git remote -v输出示例: origin https://…...
【1】跨越技术栈鸿沟:字节跳动开源TRAE AI编程IDE的实战体验
2024年初,人工智能编程工具领域发生了一次静默的变革。当字节跳动宣布退出其TRAE项目(一款融合大型语言模型能力的云端AI编程IDE)时,技术社区曾短暂叹息。然而这一退场并非终点——通过开源社区的接力,TRAE在WayToAGI等…...
FTXUI::Dom 模块
DOM 模块定义了分层的 FTXUI::Element 树,可用于构建复杂的终端界面,支持响应终端尺寸变化。 namespace ftxui {...// 定义文档 定义布局盒子 Element document vbox({// 设置文本 设置加粗 设置文本颜色text("The window") | bold | color(…...
