Rust学习笔记:Box<T>智能指针
以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正
简介
Box<T>
智能指针的组成
在Rust的智能指针中,Box<T>
是最简单的智能指针,其主要由两部分组成:(1)储存在堆内存(heap)上的数据、(2)储存在栈内存(stack)上的、指向堆内存数据的指针 ,比如下面的例子中,我们使用Box::new()
在堆内存上请求了一块区域来存储数字”5”,然后返回了一个指向该区域的指针
并赋值给变量”a”
1 | fn main() { |
Box<T>
指针的使用场景
- 在编译时,某类型的大小无法确定。但使用该类型时,上下文却需要知道其确切大小
- 当存在大量数据想要移交所有权,但需确保操作时数据不会被复制
- 当使用某个值时,只关心其是否满足特定的Trait约束,而不关心其具体的类型
总体而言,Box<T>
的作用主要有:(1)动态分配内存、(2)所有权转移、(3)避免内存泄漏、(4)处理递归类型、(5)实现Trait对象(见Trait对象部分)
使用Box<T>
指针创建递归类型
我们首先必须了解这样一个规定:Rust在编译时,必须知道一个类型所占用的空间大小 ,而递归类型(如链表)因为递归层数的不确定,其大小在编译时也是无法确定的,比如下面的代码就无法通过编译,因为List
所占用的空间大小在编译时没法确定
1 | use crate::List::{Cons, Nil}; |
但是,我们知道在Rust中指针变量的大小是可以确定的,在这里就可以使用Box<T>
来定义递归类型
1 | use crate::List::{Cons, Nil}; |
两个基本trait
Deref trait
- Deref trait的作用:Deref trait可以让用户自定义
解引用运算符*
的行为,即通过实现Deref trait,用户可以像使用常规引用一样使用智能指针
Box<T>
的解引用
1 | fn main() { |
自定义Box<T>
指针
在Rust中,Box<T>
被定义为拥有一个元素的元组结构体(tuple struct)
1 | use std::ops::Deref; |
隐式解引用转化(Deref Coercion)
隐式解引用转化(Deref Coercion)是为函数和方法提供的一种便捷特性 ,具体来说就是:如果一个类型T实现了Deref trait,那么在编译时,编译器就会将T的引用隐式地转换为经过Deref操作后生成的引用
1 | use std::ops::Deref; |
在上面的代码中,变量x
经过了两次Deref转换,即:&Mybox<String> -> &String -> &str
(注:在标准库中String实现了Deref trait,所以它会隐式地转换为&str)
Drop trait
- Drop trait的作用:实现Drop trait,可以让用户
自定义当值将要离开作用域时发生的动作
,比如文件或其他资源的释放等,它有点像Python中的__del__
方法
drop()方法的触发
1 | struct CustomSmartPointer { |
通过输出信息可以知道:程序运行完成后,内存中变量释放的顺序是从下往上的
手动释放内存
在Rust中,我们并不能直接调用某类型的drop()方法,但一般也不需要手动调用drop()方法,因为Drop trait的功能是自动运行变量被释放后的一系列行为
,但有时我们又确实存在手动释放内存的需求,这时我们可以使用标准库提供的std::mem::drop 函数
1 | use std::mem::drop; |
可以发现打印的信息顺序发生了变化,这是因为我们先显式地释放了变量c
,c
在释放时触发了c.drop()方法,所以最后的结果表现不一样
其他智能指针
除Box<T>
外,Rust中还存在其他的智能指针,如Rc<T>
、Arc<T>
、Mutex<T>
等等,这些就等用到的时候再写吧…