参考章节《Rust 程序设计语言》第15.1章 使用Box <T>指向堆上的数据
什么是智能指针?
先说说我的一个直观感觉,智能指针就是一个有特殊功能的结构体
,然后感兴趣可以去看看知乎对该问题的回答
Box 智能指针
最简单直接的智能指针是
box
,其类型是Box<T>
。box
允许你将一个值放在堆上而不是栈上。留在栈上的则是指向堆数据的指针。
Box<T>
类型是一个智能指针,因为它实现了 Deref trait
,它允许 Box<T>
值被当作引用对待。
Box<T>
值离开作用域时,由于 Box<T>
类型 也实现了 Drop trait
,因此 box
所指向的堆数据也会被清除。
Box
最简单的用法
|
|
什么时候使用Box
书上给出了三种情况,不过我觉得不太好理解
- 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候
- 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候
- 当希望拥有一个值并只关心它的类型是否实现了特定
trait
而不是其具体类型的时候PS: 这句话我没看懂,后面这句 "而不是其具体类型" 是什么意思
先说说第一种情况
当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候
怎么理解这句话?
编译时未知大小的类型
很好理解,首先 Rust
需要在编译时知道类型占用多少空间。
需要确切大小的上下文
比如当你要定义一个递归
的时候,递归是编译时不确定大小的
,但定义时
由于上面的条件,因此需要确切大小
一种无法在编译时知道大小的类型是
递归类型
,其值的一部分可以是相同类型的另一个值。这种值的嵌套理论上可以无限的进行下去,
所以 Rust 不知道递归类型需要多少空间。不过box
有一个已知的大小,所以通过在循环类型定义中插入box
,就可以创建递归类型了。
我们来看一个例子
|
|
这段代码不能编译,原因是 List 的一个成员被定义为是递归的:它直接存放了另一个相同类型的值。这意味着 Rust 无法计算为了存放 List 值到底需要多少空间。
此时我们就可以用 box
来解决这个问题
|
|
因为 Box<T>
是一个指针,我们总是知道它需要多少空间:指针的大小并不会根据其指向的数据量而改变。
Cons
成员将会需要一个i32
的大小加上储存box
指针数据的空间。Nil
成员不储存值,所以它比Cons
成员需要更少的空间。
现在我们知道了任何List
值最多需要一个i32
加上box
指针数据的大小。
通过使用box
,打破了这无限递归的连锁,这样编译器就能够计算出储存List
值需要的大小了。