当前位置: 首页 > article >正文

【Rust自学】15.2. Deref trait Pt.1:什么是Deref、解引用运算符*与实现Deref trait

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
请添加图片描述

15.2.1. 什么是Deref trait

Deref的全写是Dereference,就是引用的英文reference加上"de"这个反义前缀,意思为解引用

如果一个类型实现了Deref trait,那么它就使我们可以自定义解引用运算符*的行为。通过实现Deref trait,智能指针可像常规引用一样来处理

15.2.2. 解引用运算符

首先强调一下,常规的引用它也是一种指针。看个例子:

fn main(){
	let x = 5;
	let y = &x;

	assert_eq!(x, 5);
	assert_eq!(*y, 5);
}
  • xi32类型,值为5;y存的是一个引用,指向x的内存地址,类型是&i32,也就是说,yx的引用。
  • 第一个断言把x和5比较,由于x里存的就是5,两者相等,所以程序会通过这个断言
  • 第二个断言把*y和5比较。y是个指针,指向一个值如果想把它指向的值取出来就是在变量名前加解引用符号*。也就是说,y的类型是&i32*y的类型是i32,由于5也是i32类型,所以*y就可以与5比较而y不行。

15.2.3. 使用Box<T>当作引用

Box<T>可以替代上例中的引用,看个例子:

fn main(){
	let x = 5;
	let y = Box::new(x);

	assert_eq!(x, 5);
	assert_eq!(*y, 5);
}

这里需要注意的是,上文的代码例和这个代码例的逻辑有点不一样:

  • 上文的y = &x是把一个指向x的指针赋给了y,是一个指向栈内存的指针(因为i32存储在栈内存中)
  • 这里的y = Box::new(x)是把x的值复制一份放到堆内存中,然后把指向堆内存中这个值的指针传给y

15.2.4. 定义自己的智能指针

Box<T>被定义为拥有一个元素的tuple struct(元组结构体,详见 5.1. 定义并实例化struct)。我们来定义一个MyBox<T>,也是一个tuple struct

struct MyBox<T>(T);

impl<T> MyBox<T> {
	fn new(x: T) -> MyBox<T> {
		MyBox(x)
	}
}
  • 首先定义了一个元组结构体MyBox,使用泛型参数T来代替实际的类型,在这个元组结构体中存储一个类型为T的值。
  • 然后通过impl块定义了一个new函数用于创建新的MyBox实例

再写主函数看看实际使用有没有问题:

fn main(){
	let x = 5;
	let y = MyBox::new(x);

	assert_eq!(x, 5);
	assert_eq!(*y, 5);
}

最后一个断言assert_eq(*y, 5)*y这里报错了,报错信息是:

Type `MyBox<{integer}>` cannot be dereferenced

类型MyBox不能被解引用。

这是因为我们没有为MyBox实现Deref trait

15.2.5. 实现Deref trait

标准库中的Deref trait要求我们实现一个deref方法:这个方法借用self,返回一个指向内部数据的引用。

以上面的代码为例,如果想要为MyBox实现Deref trait(也就是实现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  
    }  
}
  • use std::ops::Deref;就是把Deref trait引入到当前作用域。
  • MyBox实现Deref trait就得写impl<T> Deref for MyBox<T>这个impl块。在这个impl块下面实现deref方法。
  • type Target = T;这种语法定义了Deref trait的关联类型。关联类型是一种稍有不同的泛型参数定义方式,以后会讲。
  • deref方法借用self,也就是&self,返回T类型的值,具体来说就是&self.0:把元组结构体的索引位置在0的元素,也就是第一个元素以引用的形式返回(其实本身也就只有一个元素)。正由于返回的是引用,所以我们可以使用*解引用运算符来访问这个值。

写主函数运行一下看看有没有问题:

fn main(){
	let x = 5;
	let y = MyBox::new(x);

	assert_eq!(x, 5);
	assert_eq!(*y, 5);
}

能够通过编译,没有问题。

而实际上主函数里的*y的写法Rust编译器会隐式地展开为:

*(y.deref())

先调用了MyBox类型上的deref方法返回一个引用,然后再使用解引用符号*进行普通的解引用操作。


http://www.kler.cn/a/518916.html

相关文章:

  • 壁纸设计过程中如何增加氛围感
  • 第 25 场 蓝桥月赛
  • C++多线程习题:非原创增加注释(02-2)
  • 矩阵快速幂
  • Kubernetes可视化界面
  • hedfs和hive数据迁移后校验脚本
  • Mongodb副本集群为什么选择3个节点不选择4个节点
  • 【Matlab高端绘图SCI绘图模板】第002期 绘制面积图
  • Spring中的事务管理器TransactionManager
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.2 ndarray解剖课:多维数组的底层实现
  • 「 机器人 」扑翼飞行器混合控制策略缺点浅谈
  • 在centos下使用containerd管理容器:5分钟从docker转型到containerd
  • go并发原语源码系列(二)sync.WaitGroup
  • How to learn html?基于chatGLM-b生成示例(仅供参考)
  • 【C++】类与对象初级应用篇:打造自定义日期类与日期计算器(2w5k字长文附源码)
  • 双注意力模块DAB详解及代码复现
  • 05.KNN算法总结
  • 重构(2)判断型函数和判断型变量的命名
  • 蓝桥杯单片机(九)定时器的进阶综合案例
  • CAPL高级应用
  • 深入理解 Window 对象:属性与方法详解
  • 模块初阶学习
  • S4 HANA更改Tax base Amount的字段控制
  • NR_shell运行流程简析
  • Pyecharts之特殊图表的独特展示
  • fatal: unable to access ‘https://github