|
|
|
@ -1,7 +1,8 @@
|
|
|
|
|
## 使用`Box<T>`指向堆上的数据
|
|
|
|
|
|
|
|
|
|
> [ch15-01-box.md](https://github.com/rust-lang/book/blob/main/src/ch15-01-box.md) <br>
|
|
|
|
|
> commit 359895c6b2e440275a663ee1a3c17e6a94fdc62b
|
|
|
|
|
> [ch15-01-box.md](https://github.com/rust-lang/book/blob/main/src/ch15-01-box.md)
|
|
|
|
|
> <br>
|
|
|
|
|
> commit 5a3a64d60b0dd786c35ca4daada7a4d20da33e5e
|
|
|
|
|
|
|
|
|
|
最简单直接的智能指针是 _box_,其类型是 `Box<T>`。box 允许你将一个值放在堆上而不是栈上。留在栈上的则是指向堆数据的指针。如果你想回顾一下栈与堆的区别请参考第四章。
|
|
|
|
|
|
|
|
|
@ -15,7 +16,7 @@
|
|
|
|
|
|
|
|
|
|
### 使用 `Box<T>` 在堆上储存数据
|
|
|
|
|
|
|
|
|
|
在讨论 `Box<T>` 的用例之前,让我们熟悉一下语法以及如何与储存在 `Box<T>` 中的值进行交互。
|
|
|
|
|
在讨论 `Box<T>` 的堆存储用例之前,让我们熟悉一下语法以及如何与储存在 `Box<T>` 中的值进行交互。
|
|
|
|
|
|
|
|
|
|
示例 15-1 展示了如何使用 box 在堆上储存一个 `i32`:
|
|
|
|
|
|
|
|
|
@ -33,19 +34,23 @@
|
|
|
|
|
|
|
|
|
|
### Box 允许创建递归类型
|
|
|
|
|
|
|
|
|
|
Rust 需要在编译时知道类型占用多少空间。一种无法在编译时知道大小的类型是 **递归类型**(_recursive type_),其值的一部分可以是相同类型的另一个值。这种值的嵌套理论上可以无限的进行下去,所以 Rust 不知道递归类型需要多少空间。不过 box 有一个已知的大小,所以通过在循环类型定义中插入 box,就可以创建递归类型了。
|
|
|
|
|
**递归类型**(_recursive type_)的值可以拥有另一个同类型的值作为其的一部分。这会产生一个问题因为 Rust 需要在编译时知道类型占用多少空间。递归类型的值嵌套理论上可以无限的进行下去,所以 Rust 不知道递归类型需要多少空间。因为 box 有一个已知的大小,所以通过在循环类型定义中插入 box,就可以创建递归类型了。
|
|
|
|
|
|
|
|
|
|
让我们探索一下 _cons list_,一个函数式编程语言中的常见类型,来展示这个(递归类型)概念。除了递归之外,我们将要定义的 cons list 类型是很直白的,所以这个例子中的概念,在任何遇到更为复杂的涉及到递归类型的场景时都很实用。
|
|
|
|
|
作为一个递归类型的例子,让我们探索一下 _cons list_。这是一个函数式编程语言中常见的数据类型,来展示这个(递归类型)概念。除了递归之外,我们将要定义的 cons list 类型是很直白的,所以这个例子中的概念,在任何遇到更为复杂的涉及到递归类型的场景时都很实用。
|
|
|
|
|
|
|
|
|
|
#### cons list 的更多内容
|
|
|
|
|
|
|
|
|
|
_cons list_ 是一个来源于 Lisp 编程语言及其方言的数据结构。在 Lisp 中,`cons` 函数(“construct function" 的缩写)利用两个参数来构造一个新的列表,他们通常是一个单独的值和另一个列表。
|
|
|
|
|
_cons list_ 是一个来源于 Lisp 编程语言及其方言的数据结构,它由嵌套的列表组成。它的名字来源于 Lisp 中的 `cons` 函数(“construct function" 的缩写),它利用两个参数来构造一个新的列表。通过对一个包含值的列表和另一个值调用 `cons`,可以构建由递归列表组成的 cons list。
|
|
|
|
|
|
|
|
|
|
cons 函数的概念涉及到更常见的函数式编程术语;“将 _x_ 与 _y_ 连接” 通常意味着构建一个新的容器而将 _x_ 的元素放在新容器的开头,其后则是容器 _y_ 的元素。
|
|
|
|
|
例如这里有一个包含列表 1,2,3 的 cons list 的伪代码表示,其每一个列表在一个括号中:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
(1, (2, (3, Nil)))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
cons list 的每一项都包含两个元素:当前项的值和下一项。其最后一项值包含一个叫做 `Nil` 的值且没有下一项。cons list 通过递归调用 `cons` 函数产生。代表递归的终止条件(base case)的规范名称是 `Nil`,它宣布列表的终止。注意这不同于第六章中的 “null” 或 “nil” 的概念,他们代表无效或缺失的值。
|
|
|
|
|
|
|
|
|
|
注意虽然函数式编程语言经常使用 cons list,但是它并不是一个 Rust 中常见的类型。大部分在 Rust 中需要列表的时候,`Vec<T>` 是一个更好的选择。其他更为复杂的递归数据类型 **确实** 在 Rust 的很多场景中很有用,不过通过以 cons list 作为开始,我们可以探索如何使用 box 毫不费力的定义一个递归数据类型。
|
|
|
|
|
cons list 并不是一个 Rust 中常见的类型。大部分在 Rust 中需要列表的时候,`Vec<T>` 是一个更好的选择。其他更为复杂的递归数据类型 **确实** 在 Rust 的很多场景中很有用,不过通过以 cons list 作为开始,我们可以探索如何使用 box 毫不费力的定义一个递归数据类型。
|
|
|
|
|
|
|
|
|
|
示例 15-2 包含一个 cons list 的枚举定义。注意这还不能编译因为这个类型没有已知的大小,之后我们会展示:
|
|
|
|
|
|
|
|
|
@ -79,7 +84,7 @@ cons list 的每一项都包含两个元素:当前项的值和下一项。其
|
|
|
|
|
|
|
|
|
|
<span class="caption">示例 15-4:尝试定义一个递归枚举时得到的错误</span>
|
|
|
|
|
|
|
|
|
|
这个错误表明这个类型 “有无限的大小”。其原因是 `List` 的一个成员被定义为是递归的:它直接存放了另一个相同类型的值。这意味着 Rust 无法计算为了存放 `List` 值到底需要多少空间。让我们一点一点来看:首先了解一下 Rust 如何决定需要多少空间来存放一个非递归类型。
|
|
|
|
|
这个错误表明这个类型 “有无限的大小”。其原因是 `List` 的一个成员被定义为是递归的:它直接存放了另一个相同类型的值。这意味着 Rust 无法计算为了存放 `List` 值到底需要多少空间。让我们拆看来看为何会得到这个错误。首先了解一下 Rust 如何决定需要多少空间来存放一个非递归类型。
|
|
|
|
|
|
|
|
|
|
### 计算非递归类型的大小
|
|
|
|
|
|
|
|
|
@ -99,16 +104,16 @@ cons list 的每一项都包含两个元素:当前项的值和下一项。其
|
|
|
|
|
|
|
|
|
|
### 使用 `Box<T>` 给递归类型一个已知的大小
|
|
|
|
|
|
|
|
|
|
Rust 无法计算出要为定义为递归的类型分配多少空间,所以编译器给出了示例 15-4 中的错误。这个错误也包括了有用的建议:
|
|
|
|
|
因为 Rust 无法计算出要为定义为递归的类型分配多少空间,所以编译器给出了一个包括了有用建议的错误:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable
|
|
|
|
|
|
|
|
|
|
|
2 | Cons(i32, Box<List>),
|
|
|
|
|
| ^^^^ ^
|
|
|
|
|
| ++++ +
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
在建议中,“indirection” 意味着不同于直接储存一个值,我们将间接的储存一个指向值的指针。
|
|
|
|
|
在建议中,“indirection” 意味着不同于直接储存一个值,应该间接的储存一个指向值的指针。
|
|
|
|
|
|
|
|
|
|
因为 `Box<T>` 是一个指针,我们总是知道它需要多少空间:指针的大小并不会根据其指向的数据量而改变。这意味着可以将 `Box` 放入 `Cons` 成员中而不是直接存放另一个 `List` 值。`Box` 会指向另一个位于堆上的 `List` 值,而不是存放在 `Cons` 成员中。从概念上讲,我们仍然有一个通过在其中 “存放” 其他列表创建的列表,不过现在实现这个概念的方式更像是一个项挨着另一项,而不是一项包含另一项。
|
|
|
|
|
|
|
|
|
@ -130,6 +135,6 @@ help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` repre
|
|
|
|
|
|
|
|
|
|
box 只提供了间接存储和堆分配;他们并没有任何其他特殊的功能,比如我们将会见到的其他智能指针。它们也没有这些特殊功能带来的性能损失,所以他们可以用于像 cons list 这样间接存储是唯一所需功能的场景。我们还将在第十七章看到 box 的更多应用场景。
|
|
|
|
|
|
|
|
|
|
`Box<T>` 类型是一个智能指针,因为它实现了 `Deref` trait,它允许 `Box<T>` 值被当作引用对待。当 `Box<T>` 值离开作用域时,由于 `Box<T>` 类型 `Drop` trait 的实现,box 所指向的堆数据也会被清除。让我们更详细的探索一下这两个 trait。这两个 trait 对于在本章余下讨论的其他智能指针所提供的功能中,将会更为重要。
|
|
|
|
|
`Box<T>` 类型是一个智能指针,因为它实现了 `Deref` trait,它允许 `Box<T>` 值被当作引用对待。当 `Box<T>` 值离开作用域时,由于 `Box<T>` 类型 `Drop` trait 的实现,box 所指向的堆数据也会被清除。这两个 trait 对于在本章余下讨论的其他智能指针所提供的功能中,将会更为重要。让我们更详细的探索一下这两个 trait。
|
|
|
|
|
|
|
|
|
|
[trait-objects]: ch17-02-trait-objects.html#为使用不同类型的值而设计的-trait-对象
|
|
|
|
|