rust智能指针

一、智能指针是什么

指针是一个存储内存地址的变量。这个地址指向一些其他数据。
智能指针是一类数据结构,它们类似指针,但是拥有额外的功能。智能指针的概念起源于C++。Rust标准库提供了许多智能指针,比如String和Vec<T>,虽然我们并不这么称呼它们,但这些类型都属于智能指针。
智能指针通常使用结构体实现。智能指针与常规结构体的区别在于智能指针实现了Deref和Drop trait。Deref trait使智能指针表现的像引用一样,这样就可以编写既用于引用、又用于智能指针的代码。Drop trait允许我们自定义智能指针离开作用域时的行为。

在Rust中,引用和智能指针的一个区别是引用是一类只借用数据的指针;智能指针则拥有数据的所有权。

二、最常用的一些智能指针

1.Box<T>,用于在堆上分配
2.Rc<T>,一个引用计数类型,其数据可以有多个所有者
3.Ref<T>RefMut<T>,通过RefCell<T> 访问(RefCell<T>是一个在运行时而不是在编译时执行借用规则的类型)

(一)Box指针
Box<T>类型是一个智能指针,因为它实现了Deref trait和Drop trait。
box把值放在堆上而不是栈上。留在栈上的则是指向堆数据的指针。除了数据被储存在堆上而不是栈上之外,box没有性能损失。不过也没有很多额外的功能。
1.创建Box
使用new函数创建
例子

fn main() {let var_i32 = 5; // 默认数据保存在 栈 上let b = Box::new(var_i32); // 使用Box后数据会存储在堆上println!("b = {}", b);
}

b离开作用域时,它将自动释放。这个释放包括b本身(位于栈上)和它所指向的数据(位于堆上)。

2.使用box
像使用引用一样使用box。
使用解引用操作符 * 解引用box

fn main() {let x = 5; // 值类型数据let y = Box::new(x); // y是一个智能指针,指向堆上存储的数据5println!("{}",5==x);println!("{}",5==*y); // 为了访问y存储的具体数据,需要解引用
}
编译运行结果如下
true
true
直接使用  5 == y  会返回false

3.使用Box创建递归类型
Rust需要在编译时知道类型占用多少空间。一种无法在编译时知道大小的类型是递归类型,其值的一部分可以是自身类型的另一个值。这种嵌套可以是无限的,所以Rust不知道递归类型需要多少空间。不过box有一个已知的大小,所以通过在递归类型定义中插入box,就可以创建递归类型了。一个常见递归类型就是链表。
实例

enum List {Cons(i32, List),Nil,
}
use crate::List::{Cons, Nil};
fn main() {let list = Cons(1, Cons(2, Cons(3, Nil)));//使用这个list来储存1, 2, 3
}

第一个Cons储存1和另一个List值。这个List是一个Cons值,此cons储存2和下一个List值。这个list又是一个cons,储存3和值为Nil的List。这段代码编译错误。因为这个类型 “有无限的大小”。
因为Box<T> 是一个指针,它的大小是确定的,所以将Box作为Cons的成员,这样List的大小就确定了。

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))))));
}

三、两个特性

(一)Deref Trait
1.Deref是由Rust标准库提供的一个特性。
实现Deref之后就能把智能指针当作引用使用,相当于重载解引用运算符*。
Deref中包含deref()方法。
deref()方法用于引用self实例并返回一个指向内部数据的指针。

例子

use std::ops::Deref;
struct DerefExample<T> {value: T
}
impl<T> Deref for DerefExample<T> {type Target = T;fn deref(&self) -> &Self::Target {&self.value}
}
let x = DerefExample { value: 'a' };
assert_eq!('a', *x);

范例

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); // 调用new() 返回创建一个结构体实例println!("5==x is {}",5==x);println!("5==*y is {}",5==*y); // 解引用yprintln!("x==*y is {}",x==*y); // 解引用y
}
编译运行结果如下
5==x is true
5==*y is true
x==*y is true

每次使用 * 时, * 运算符都被替换成先调用deref方法再使用 * 解引用的操作,且只会发生一次,不会无限递归替换 * 操作符,解引用出i32类型的值就停止了

2.DerefMut trait用于重载可变引用的 * 运算符

3.Deref隐式转换
Deref隐式转换将实现了Deref的类型的引用转换为另一种类型的引用。例如,将&String转换为&str,因为String实现了Deref因此可以返回&str。Deref强制转换是Rust在函数或方法传参上的一种便利操作,并且只能作用于实现了Deref的类型。当这种特定类型的引用作为实参传递给和形参类型不同的函数时将自动转换类型。这时会有一系列的deref方法被调用,把我们提供的类型转换成了形参所需的类型。
Deref隐式转换使Rust程序员在调用函数时无需使用过多& 和 *。这个功能方便我们编写同时作用于引用或智能指针的代码。

实例

//还是上面的MyBox<T>
fn hello(name: &str) {println!("Hello, {name}!");
}
let m = MyBox::new(String::from("Rust"));
hello(&m);

因为Deref隐式转换,使用MyBox<String>的引用作为参数是可行的。
因为MyBox<T>实现了Deref,Rust可以通过deref将&MyBox<String>变为&String。而String也实现了Deref,Rust再次调用deref将&String变为&str,这就符合hello函数的定义了。

如果没有Deref强制转换,要把&MyBox<String>类型的值传给hello函数,则不得不编写如下代码

let m = MyBox::new(String::from("Rust"));
hello(&(*m)[..]);

(*m)MyBox<String>解引用为String,接着&和[..]将String转换成&str。
没有Deref强制转换的话,所有这些符号混在一起将难以读写和理解。Deref强制转换会自动执行这些转换。这些转换发生在编译时,所以没有运行时损耗!

Deref隐式转换有三种情形:
(1)当T实现Deref Target=U 时从 &T到 &U。
(2)当T实现DerefMut Target=U 时从 &mut T到 &mut U。
(3)当T实现Deref Target=U 时从 &mut T到 &U。
第一种情况表明如果有一个 &T,而T实现了返回U类型的Deref,则可以直接得到 &U。
第二种情况表明可变引用也有着相同的行为。
第三个情况将可变引用强转为不可变引用。但是反过来是不行的,不可变引用永远也不能强转为可变引用。
这三种情况下,T类型都自动实现了U类型的所有方法。

(二)Drop Trait
Rust中的析构函数是由Drop trait提供的drop()方法。
Drop Trait只有一个方法drop() 。
实现了Drop特质的结构体在离开了它的作用域时会调用drop()方法。
例子

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}
}
impl<T> Drop for MyBox<T>{fn drop(&mut self){println!("dropping MyBox object from memory ");}
}
fn main() {let x = 50;MyBox::new(x);MyBox::new("Hello");
}
编译运行结果如下
dropping MyBox object from memory
dropping MyBox object from memory

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

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

相关文章

【React】React组件生命周期以及触发顺序(部分与vue做比较)

最近在学习React&#xff0c;发现其中的生命周期跟Vue有一些共同点&#xff0c;但也有比较明显的区别&#xff0c;并且执行顺序也值得讨论一下&#xff0c;于是总结了一些资料在这里&#xff0c;作为学习记录。 v17.0.1后生命周期图片 初始化阶段 由ReactDOM.render()触发 —…

百度交易中台之内容分润结算系统架构浅析

作者 | 交易中台团队 导读 随着公司内容生态的蓬勃发展&#xff0c;内容产出方和流量提供方最关注的“收益结算”的工作&#xff0c;也就成为重中之重。本文基于内容分润结算业务为入口&#xff0c;介绍了实现过程中的重难点&#xff0c;比如千万级和百万级数据量下的技术选型和…

用Java打印长方形、平行四边形 、三角形、菱形、空心菱形

今天复习使用嵌套for来完成一些任务&#xff0c;于是想着打印一些图形来练习 思考感悟 长方形 行数 和 每行的星星数嵌套遍历即可 平行四边形 核心&#xff1a;每行空格数总行数-行数 行数空格数132231 三角形 核心&#xff1a;每行星星数2*当前行数-1 行数星星数1123…

【VIM】VIm-plug插件

如何查找需要的插件 https://github.com/mhinz/vim-startify https://github.com/vim-airline/vim-airline https://github.com/Yggdroot/indentLine github.com/w0ng/vim-hybrid github.com/altercationi/vim-colors-solarized guithub.com/morhetz/gruvbox github.com/sc…

kotlin aes 加密解密

文章目录 1. key填充2. 加密3. 解密 1. key填充 aes算法对key的字节数有要求 所以对输入的key要做填充处理 fun fillKey(key: String): ByteArray {val random SecureRandom.getInstance("SHA1PRNG")random.setSeed(key.toByteArray())val generator KeyGenerato…

【2023保研】双非上岸东南网安

个人情况 学校&#xff1a;henu 专业&#xff1a;信息安全 排名&#xff1a;1/66 英语&#xff1a;六级500 竞赛&#xff1a;蓝桥杯PB国一&#xff0c;ISCC国一&#xff0c;密码数学挑战赛国三&#xff0c;还有其他一些省级水奖 论文&#xff1a;一篇EI在投&#xff08;三作通…

C++:红黑树

目录 一、关于红黑树 1、红黑树和AVL树区别 2、红黑树规则 二、红黑树 1、红黑树变色举例 2、红黑树具体情况分析 ①、情况一&#xff1a;只变色 ②、情况二&#xff1a;单旋 变色 ③、情况三&#xff1a;双旋 变色 三、红黑树的模拟实现 一、关于红黑树 1、红黑树…

王道考研计算机网络——传输层

一、传输层概述 复用&#xff1a;发送方不同的应用进程都可以使用同一个传输层的协议来传送数据 分用&#xff1a;接收方的传输层在去除报文段的首部之后能把数据交给正确的应用进程 熟知端口号就是知名端口号0-1023 客户端使用的端口号是动态变化的&#xff0c;不是唯一确定…

某房产网站登录RSA加密分析

文章目录 1. 写在前面2. 抓包分析3. 扣加密代码4. 还原加密 1. 写在前面 今天是国庆节&#xff0c;首先祝福看到这篇文章的每一个人节日快乐&#xff01;假期会老的这些天一直在忙事情跟日常带娃&#xff0c;抽不出一点时间来写东西。夜深了、娃也睡了。最近湖南开始降温了&…

Unity Urp无线延申的网格效果

无线延申的网格 该项目必须是再Urp项目 shader代码实现 Shader "Unlit/infTutorial1" {Properties{_Alpha ("Alpha", Range(0, 0.5)) 0.5}SubShader{Tags{"RenderPipeline""UniversalRenderPipeline""RenderType""…

Pikachu靶场——PHP反序列化漏洞

文章目录 1. PHP反序列化1.1 反序列化代码审计1.2 漏洞防御 1. PHP反序列化 可参考我写的另一篇博客&#xff1a;反序列化漏洞及漏洞复现。 序列化serialize() 序列化说通俗点就是把一个对象变成可以传输的字符串&#xff0c;比如下面是一个对象&#xff1a; class S{publi…

【MySQL】初识数据库

目录 ♫什么是数据库 ♫数据库的分类 ♪关系型数据库 ♪非关系型数据库 ♫显示已有的数据库 ♫创建数据库 ♫删除数据库 ♫使用数据库 ♫数据类型 ♪数值类型 ♪字符串类型 ♪日期类型 ♫创建表 ♫查看表 ♪查看当前数据库下所以表&#xff1a; ♪查看指定表结构&#x…

java多线程相关介绍

1. 线程的创建和启动 在 Java 中创建线程有两种方式。一种是继承 Thread 类并重写其中的 run() 方法&#xff0c;另一种是实现 Runnable 接口并重写其中的 run() 方法。创建完线程对象后&#xff0c;调用 start() 方法可以启动线程。 2. 线程的状态 Java 的线程在不同阶段会处于…

SimpleCG动画示例--汉诺塔动画演示

前言 SimpleCG的使用方法在前面已经介绍了许多&#xff0c;有兴趣的同学如果有去动手&#xff0c;制作一些简单动画应该没多大问题的。所以这次我们来演示一下简单动画。我们刚学习C语言的递归函数时&#xff0c;有一个经典例子相信很多同学都写过&#xff0c;那就是汉诺塔。那…

hadoop生态现状、介绍、部署

一、引出hadoop 1、hadoop的高薪现状 各招聘平台都有许多hadoop高薪职位&#xff0c;可以看看职位所需求的技能 ----> hadoop是什么&#xff0c;为什么会这么高薪&#xff1f;引出大数据&#xff0c;大数据时代&#xff0c;大数据与云计算 2、大数据时代的介绍 大数据的故事…

buuctf-[WUSTCTF2020]CV Maker

打开环境 随便登录注册一下 进入到了profile.php 其他没有什么页面&#xff0c;只能更换头像上传文件&#xff0c;所以猜测是文件上传漏洞 上传一句话木马看看 <?php eval($_POST[a]);?>回显 搜索一下 添加文件头GIF89a。上传php文件 查看页面源代码&#xff0c;看…

【分布式云储存】Springboot微服务接入MinIO实现文件服务

文章目录 前言技术回顾准备工作申请accessKey\secretKey创建数据存储桶公共资源直接访问测试 接入springboot实现文件服务依赖引入配置文件MinIO配置MinIO工具类 OkHttpSSLSocketClient兼容ssl静态资源预览解决方案资源上传预览测试测试结果 前言 上篇博客我们介绍了分布式云存…

Unity之Hololens如何升级MRTK内置shader支持URP

一.前言 什么是Hololens? Hololens是由微软开发的一款混合现实头戴式设备,它将虚拟内容与现实世界相结合,为用户提供了沉浸式的AR体验。Hololens通过内置的传感器和摄像头,能够感知用户的环境,并在用户的视野中显示虚拟对象。这使得用户可以与虚拟内容进行互动,将数字信…

php时间选择器插件与安全过滤参数发生空格冲突

php参数过滤时&#xff0c;将“ ”作为隐患予以禁止&#xff0c;但是在时间传递时&#xff0c;如2023-09-30 10:00:00作为变量传递时&#xff0c;被禁止。 php安全参数过滤 function safe_replace($str) {$disallow_str array(%27, %2527, *, ", "", ;, <…

Qt Creator 预览界面 快捷键

一般来说&#xff0c;我们运行Qt程序所花费的时间是比较长的&#xff0c;那有时我们只改变了界面&#xff0c;那么此时花费如此长的时间去运行程序来观察界面改动的效果是非常浪费时间的行为。 此时我们可以选择预览界面来观察界面改动后的效果&#xff1a;