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 函数和方法的隐式解引用强制转换

  • 解引用强制转换只能工作在实现了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提供了DerefMut trait用于重载可变引用的*运算符;
  • Rust 在发现类型和 trait 的实现满足以下三种情况时会进行解引用强制转换;
    1. 当 T: Deref<Target=U> :从 &T 到 &U
      如果有一个&T,而T实现了返回U类型的Deref,则可以直接得到&U
    2. 当 T: DerefMut<Target=U> :从 &mut T 到 &mut U
      对于可变引用有着与第一种相同的行为;
    3. 当 T: Deref<Target=U> :从 &mut T 到 &U
      Rust也会将可变引用强转为不可变引用,但是反之是不可能的,因为不可变引用永远也不能强转为可变引用;

四、使用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被提前析构;
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv8中的C2f模块

文章目录 一、结构概述二、模块功能 一、结构概述 C2f块:首先由一个卷积块(Conv)组成&#xff0c;该卷积块接收输入特征图并生成中间特征图特征图拆分:生成的中间特征图被拆分成两部分&#xff0c;一部分直接传递到最终的Concat块&#xff0c;另一部分传递到多个Botleneck块进…

用VScode打开keil下的文件中文编码乱码的问题,以及利用VScode转换字符编码的方法

目录 问题描述 解决方法 利用VScode转换字符编码的方法 问题描述 keil中默认的编码是ANIS如下图所示。 而VScode中默认的编码为UTF-8 &#xff0c;打开后如下。 解决方法 建议另存后&#xff0c;再打开目标文件&#xff0c;防止误操作&#xff01; 在VScode的最下方可以找…

一文读懂数据仓库ODS层

数据仓库一般分为三层&#xff0c;分别为数据贴源层&#xff08;ODS&#xff0c;Operation Data Store&#xff09;、数据公共层&#xff08;CDM&#xff0c;Common Data Model&#xff09;和数据应用层&#xff08;ADS&#xff0c;Application Data Service&#xff09;。其中…

【windows|008】DNS服务详解

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 ​ &#x1f3c5;阿里云ACE认证高级工程师 ​ &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社…

多模态MLLM都是怎么实现的(10)-Chameleon和Florence-2如果你想玩多模态就不能不了解

这个也是一个补充文&#xff0c;前9章基本把该讲的讲了&#xff0c;今天这个内容主要是因为Meta出了一个Chameleon&#xff0c;这个以后可能会成为LLaMA的一个很好的补充&#xff0c;或者说都有可能统一起来&#xff0c;叫LLaMA或者Chamleon或者什么别的&#xff0c;另外我司把…

MSPM0G3507——PWM

在sysconfig中&#xff0c;左侧可以选择MCU的外设&#xff0c;我们找到并点击TIMER-PWM选项卡&#xff0c;在TIMER-PWM中点击ADD&#xff0c;就可以添加定时器下的PWM外设。 这里设置通道0为100Hz的频率&#xff0c;0%占空比的PWM&#xff0c;周期计数值为1000&#xff0c;比较…

聚类算法(2)--- ISODATA算法

本篇文章是博主在人工智能等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在AI学习笔记&#…

专业技能篇---计算机网络

文章目录 前言计算机网络基础一、网络分层模型 HTTP一、从输入URL到页面显示发生了什么&#xff1f;二、Http的状态码有哪些&#xff1f;三、 HTTP与HTTPS有什么区别&#xff1f;四、URI 和 URL 的区别是什么?五、Cookie和Session有什么区别&#xff1f;六、GET与POST WebSock…

初阶 《数组》 4. 数组作为函数参数

4. 数组作为函数参数 往往我们在写代码的时候&#xff0c;会将数组作为参数传给函数。比如&#xff1a;我要实现一个冒泡排序函数&#xff08;将一个整形数组排序&#xff09; 4.1 冒泡排序函数的错误设计 #include <stdio.h> void bubble_sort(int arr[]) {int sz s…

智慧工厂监控可视化解决方案(160页WORD)

方案介绍&#xff1a; 本智慧工厂监控可视化解决方案通过集成先进的物联网和大数据技术&#xff0c;为制造业企业提供了全面的数字化转型支持。通过实时监控、数据分析、可视化展示等功能&#xff0c;帮助企业提升生产效率、降低运营成本、优化产品质量和能源利用率&#xff0…

【Python】从基础到进阶(一):了解Python语言基础以及变量的相关知识

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 引言一、Python简介1.1 历史背景1.2 设计哲学1.3 语言特性1.4 应用场景1.5 为什么选择Python 二、Python语言基础2.1 注释规则2.1.1 单行注释2.1.2 多行注释2.1.3 文件编码声明注释 2.2 代码缩进2.3 编码规范2.3.1 命名规范…

【pytorch02】手写数字问题引入

1.数据集 现实生活中遇到的问题 车牌识别身份证号码识别快递单的识别 都会涉及到数字识别 MNIST&#xff08;收集了很多人手写的0到9数字的图片&#xff09; 每个数字拥有7000个图像train/test splitting:60k vs 10k 图片大小28 28 数据集划分成训练集和测试集合的意义…

springboot + Vue前后端项目(第十七记)

项目实战第十七记 写在前面1. 个人信息1.1 Person.vue1.2 设置路由并改动Header.vue1.3 动态刷新头像1.3.1 在保存个人信息时&#xff0c;触发方法1.3.2 父组件Manage.vue1.3.3 再将user以prop方式传递给子组件Header.vue1.3.4 Header.vue使用user 1.4 效果图 2. 修改密码2.1 前…

配置 python 脚本操作Excel 环境

在已装python的前提下 一、安装依赖库 pip install pandas pip install openpyxl安装完后&#xff0c;可以在 Python 中运行以下命令来查看 pandas 或 openpyxl 的安装路径&#xff1a; import pandas as pd print(pd.__path__)import openpyxl print(openpyxl.__path__)二、测…

【CT】LeetCode手撕—415. 字符串相加

目录 题目1- 思路2- 实现⭐415. 字符串相加——题解思路 3- ACM 实现 题目 原题连接&#xff1a;415. 字符串相加 1- 思路 模式识别&#xff1a;字符串相加 逆向遍历过程模拟 数据结构 ① String res &#xff1a;记录res 、② carry 记录进位值① 定义两个整数遍历 nums1 …

本地项目上传到gitee

本地项目通过webstorm上传到gitee 1.登录gitee选择新建仓库 2.输入新建仓库的名字&#xff08;名字与本地项目名一致&#xff09; 3.复制链接 4.找到本地项目&#xff0c;选中地址输入cmd打开命令提示框 5.输入git init初始化git&#xff0c;生成.git文件 6.webstorm中打开项目…

Android修行手册-ImageView的adjustViewBounds和设置透明度

点击跳转>GameFramework文档系列&#xff08;二&#xff09;- 场景相关 点击跳转>GameFramework文档系列&#xff08;三&#xff09;- 日志管理和UI 点击跳转>GameFramework文档系列&#xff08;四&#xff09;- 事件订阅 点击跳转>保姆式Cocos合成大西瓜案例 …

JDK19特性

JDK19特性 一、JAVA19概述 JDK 19 2022 年 9 月 20 日正式发布以供生产使用,非长期支持版本。不过,JDK 19 中有一些比较重要的新特性值得关注。 JDK 19 只有 7 个新特性: JEP 405: Record Patterns(记录模式)[1] (预览)JEP 422: Linux/RISC-V Port[2]JEP 424: Foreign …

qt 一个可以拖拽的矩形

1.概要 2.代码 2.1 mycotrl.h #ifndef MYCOTRL_H #define MYCOTRL_H#include <QWidget> #include <QMouseEvent>class MyCotrl: public QWidget {Q_OBJECT public://MyCotrl();MyCotrl(QWidget *parent nullptr); protected:void paintEvent(QPaintEvent *even…

DVWA 靶场 CSP Bypass 通关解析

前言 DVWA代表Damn Vulnerable Web Application&#xff0c;是一个用于学习和练习Web应用程序漏洞的开源漏洞应用程序。它被设计成一个易于安装和配置的漏洞应用程序&#xff0c;旨在帮助安全专业人员和爱好者了解和熟悉不同类型的Web应用程序漏洞。 DVWA提供了一系列的漏洞场…