diff --git a/docs/ch01-00-introduction.html b/docs/ch01-00-introduction.html index d848f29..1d7caaf 100644 --- a/docs/ch01-00-introduction.html +++ b/docs/ch01-00-introduction.html @@ -47,7 +47,7 @@
diff --git a/docs/ch01-01-installation.html b/docs/ch01-01-installation.html index 90c9540..cbfaf05 100644 --- a/docs/ch01-01-installation.html +++ b/docs/ch01-01-installation.html @@ -47,7 +47,7 @@
diff --git a/docs/ch01-02-hello-world.html b/docs/ch01-02-hello-world.html index a24e07b..314cb19 100644 --- a/docs/ch01-02-hello-world.html +++ b/docs/ch01-02-hello-world.html @@ -47,7 +47,7 @@
diff --git a/docs/ch02-00-guessing-game-tutorial.html b/docs/ch02-00-guessing-game-tutorial.html index 179abd6..bf621fa 100644 --- a/docs/ch02-00-guessing-game-tutorial.html +++ b/docs/ch02-00-guessing-game-tutorial.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-00-common-programming-concepts.html b/docs/ch03-00-common-programming-concepts.html index ade8f0b..96019ab 100644 --- a/docs/ch03-00-common-programming-concepts.html +++ b/docs/ch03-00-common-programming-concepts.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-01-variables-and-mutability.html b/docs/ch03-01-variables-and-mutability.html index 48e17a6..c5095b0 100644 --- a/docs/ch03-01-variables-and-mutability.html +++ b/docs/ch03-01-variables-and-mutability.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-02-data-types.html b/docs/ch03-02-data-types.html index da275dc..abaa629 100644 --- a/docs/ch03-02-data-types.html +++ b/docs/ch03-02-data-types.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-03-how-functions-work.html b/docs/ch03-03-how-functions-work.html index ce1bf37..9c7f536 100644 --- a/docs/ch03-03-how-functions-work.html +++ b/docs/ch03-03-how-functions-work.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-04-comments.html b/docs/ch03-04-comments.html index c52cc88..b460acb 100644 --- a/docs/ch03-04-comments.html +++ b/docs/ch03-04-comments.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-05-control-flow.html b/docs/ch03-05-control-flow.html index 3a52644..c856ae7 100644 --- a/docs/ch03-05-control-flow.html +++ b/docs/ch03-05-control-flow.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-00-understanding-ownership.html b/docs/ch04-00-understanding-ownership.html index e3b3ec8..1881367 100644 --- a/docs/ch04-00-understanding-ownership.html +++ b/docs/ch04-00-understanding-ownership.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-01-what-is-ownership.html b/docs/ch04-01-what-is-ownership.html index 785f87b..04f732f 100644 --- a/docs/ch04-01-what-is-ownership.html +++ b/docs/ch04-01-what-is-ownership.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-02-references-and-borrowing.html b/docs/ch04-02-references-and-borrowing.html index bb8b1d2..6529d44 100644 --- a/docs/ch04-02-references-and-borrowing.html +++ b/docs/ch04-02-references-and-borrowing.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-03-slices.html b/docs/ch04-03-slices.html index ff74caf..2b78660 100644 --- a/docs/ch04-03-slices.html +++ b/docs/ch04-03-slices.html @@ -47,7 +47,7 @@
diff --git a/docs/ch05-00-structs.html b/docs/ch05-00-structs.html index 8cc18a8..3ae1224 100644 --- a/docs/ch05-00-structs.html +++ b/docs/ch05-00-structs.html @@ -47,7 +47,7 @@
diff --git a/docs/ch05-01-method-syntax.html b/docs/ch05-01-method-syntax.html index 538f5d4..414a3d2 100644 --- a/docs/ch05-01-method-syntax.html +++ b/docs/ch05-01-method-syntax.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-00-enums.html b/docs/ch06-00-enums.html index 9cf4d91..1d2ea85 100644 --- a/docs/ch06-00-enums.html +++ b/docs/ch06-00-enums.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-01-defining-an-enum.html b/docs/ch06-01-defining-an-enum.html index 6732e9e..98fede1 100644 --- a/docs/ch06-01-defining-an-enum.html +++ b/docs/ch06-01-defining-an-enum.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-02-match.html b/docs/ch06-02-match.html index dcf76e4..ffada68 100644 --- a/docs/ch06-02-match.html +++ b/docs/ch06-02-match.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-03-if-let.html b/docs/ch06-03-if-let.html index 5159549..930d73b 100644 --- a/docs/ch06-03-if-let.html +++ b/docs/ch06-03-if-let.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-00-modules.html b/docs/ch07-00-modules.html index d7f04eb..a628758 100644 --- a/docs/ch07-00-modules.html +++ b/docs/ch07-00-modules.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-01-mod-and-the-filesystem.html b/docs/ch07-01-mod-and-the-filesystem.html index 3af40dc..33bf5d3 100644 --- a/docs/ch07-01-mod-and-the-filesystem.html +++ b/docs/ch07-01-mod-and-the-filesystem.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-02-controlling-visibility-with-pub.html b/docs/ch07-02-controlling-visibility-with-pub.html index 5cd37dd..2850566 100644 --- a/docs/ch07-02-controlling-visibility-with-pub.html +++ b/docs/ch07-02-controlling-visibility-with-pub.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-03-importing-names-with-use.html b/docs/ch07-03-importing-names-with-use.html index ef91fa0..0d0ba09 100644 --- a/docs/ch07-03-importing-names-with-use.html +++ b/docs/ch07-03-importing-names-with-use.html @@ -47,7 +47,7 @@
diff --git a/docs/ch08-00-common-collections.html b/docs/ch08-00-common-collections.html index 3ed7ff1..b3e1859 100644 --- a/docs/ch08-00-common-collections.html +++ b/docs/ch08-00-common-collections.html @@ -47,7 +47,7 @@
diff --git a/docs/ch08-01-vectors.html b/docs/ch08-01-vectors.html index b1dae55..b301ee3 100644 --- a/docs/ch08-01-vectors.html +++ b/docs/ch08-01-vectors.html @@ -47,7 +47,7 @@
diff --git a/docs/ch08-02-strings.html b/docs/ch08-02-strings.html index 8c3ca81..2642d16 100644 --- a/docs/ch08-02-strings.html +++ b/docs/ch08-02-strings.html @@ -47,7 +47,7 @@
diff --git a/docs/ch08-03-hash-maps.html b/docs/ch08-03-hash-maps.html index 0293495..1944409 100644 --- a/docs/ch08-03-hash-maps.html +++ b/docs/ch08-03-hash-maps.html @@ -47,7 +47,7 @@
diff --git a/docs/ch09-00-error-handling.html b/docs/ch09-00-error-handling.html index 07c557b..76d97c2 100644 --- a/docs/ch09-00-error-handling.html +++ b/docs/ch09-00-error-handling.html @@ -47,7 +47,7 @@
diff --git a/docs/ch09-01-unrecoverable-errors-with-panic.html b/docs/ch09-01-unrecoverable-errors-with-panic.html index 16d5445..4de9663 100644 --- a/docs/ch09-01-unrecoverable-errors-with-panic.html +++ b/docs/ch09-01-unrecoverable-errors-with-panic.html @@ -47,7 +47,7 @@
diff --git a/docs/ch09-02-recoverable-errors-with-result.html b/docs/ch09-02-recoverable-errors-with-result.html index 5cc995e..c7871d1 100644 --- a/docs/ch09-02-recoverable-errors-with-result.html +++ b/docs/ch09-02-recoverable-errors-with-result.html @@ -47,7 +47,7 @@
diff --git a/docs/ch09-03-to-panic-or-not-to-panic.html b/docs/ch09-03-to-panic-or-not-to-panic.html index 328a5de..f1841a2 100644 --- a/docs/ch09-03-to-panic-or-not-to-panic.html +++ b/docs/ch09-03-to-panic-or-not-to-panic.html @@ -47,7 +47,7 @@
diff --git a/docs/ch10-00-generics.html b/docs/ch10-00-generics.html index 6044bb4..48c9bdc 100644 --- a/docs/ch10-00-generics.html +++ b/docs/ch10-00-generics.html @@ -47,7 +47,7 @@
diff --git a/docs/ch10-01-syntax.html b/docs/ch10-01-syntax.html index 05870b6..26d079b 100644 --- a/docs/ch10-01-syntax.html +++ b/docs/ch10-01-syntax.html @@ -47,7 +47,7 @@
diff --git a/docs/ch10-02-traits.html b/docs/ch10-02-traits.html index b3d8dac..9b40182 100644 --- a/docs/ch10-02-traits.html +++ b/docs/ch10-02-traits.html @@ -47,7 +47,7 @@
diff --git a/docs/ch10-03-lifetime-syntax.html b/docs/ch10-03-lifetime-syntax.html index 5f2b2bb..21e4879 100644 --- a/docs/ch10-03-lifetime-syntax.html +++ b/docs/ch10-03-lifetime-syntax.html @@ -47,7 +47,7 @@
diff --git a/docs/ch11-00-testing.html b/docs/ch11-00-testing.html index aa4cc70..a904a69 100644 --- a/docs/ch11-00-testing.html +++ b/docs/ch11-00-testing.html @@ -47,7 +47,7 @@
diff --git a/docs/ch11-01-writing-tests.html b/docs/ch11-01-writing-tests.html index f42bcec..e82b4dd 100644 --- a/docs/ch11-01-writing-tests.html +++ b/docs/ch11-01-writing-tests.html @@ -47,7 +47,7 @@
diff --git a/docs/ch11-02-running-tests.html b/docs/ch11-02-running-tests.html index 8a72e84..80540a3 100644 --- a/docs/ch11-02-running-tests.html +++ b/docs/ch11-02-running-tests.html @@ -47,7 +47,7 @@
diff --git a/docs/ch11-03-test-organization.html b/docs/ch11-03-test-organization.html index 42f871d..2f5b0cc 100644 --- a/docs/ch11-03-test-organization.html +++ b/docs/ch11-03-test-organization.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-00-an-io-project.html b/docs/ch12-00-an-io-project.html index 209b7b2..8db3e6d 100644 --- a/docs/ch12-00-an-io-project.html +++ b/docs/ch12-00-an-io-project.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-01-accepting-command-line-arguments.html b/docs/ch12-01-accepting-command-line-arguments.html index 878ecfb..148533a 100644 --- a/docs/ch12-01-accepting-command-line-arguments.html +++ b/docs/ch12-01-accepting-command-line-arguments.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-02-reading-a-file.html b/docs/ch12-02-reading-a-file.html index 895c376..96ae2da 100644 --- a/docs/ch12-02-reading-a-file.html +++ b/docs/ch12-02-reading-a-file.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-03-improving-error-handling-and-modularity.html b/docs/ch12-03-improving-error-handling-and-modularity.html index f85cb05..aea8a60 100644 --- a/docs/ch12-03-improving-error-handling-and-modularity.html +++ b/docs/ch12-03-improving-error-handling-and-modularity.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-04-testing-the-librarys-functionality.html b/docs/ch12-04-testing-the-librarys-functionality.html index 63d76bd..6817002 100644 --- a/docs/ch12-04-testing-the-librarys-functionality.html +++ b/docs/ch12-04-testing-the-librarys-functionality.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-05-working-with-environment-variables.html b/docs/ch12-05-working-with-environment-variables.html index 5b3bf99..9c07b53 100644 --- a/docs/ch12-05-working-with-environment-variables.html +++ b/docs/ch12-05-working-with-environment-variables.html @@ -47,7 +47,7 @@
diff --git a/docs/ch12-06-writing-to-stderr-instead-of-stdout.html b/docs/ch12-06-writing-to-stderr-instead-of-stdout.html index 067d225..90915b5 100644 --- a/docs/ch12-06-writing-to-stderr-instead-of-stdout.html +++ b/docs/ch12-06-writing-to-stderr-instead-of-stdout.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-00-functional-features.html b/docs/ch13-00-functional-features.html index 012dd7e..7bab7f5 100644 --- a/docs/ch13-00-functional-features.html +++ b/docs/ch13-00-functional-features.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-01-closures.html b/docs/ch13-01-closures.html index 32570f2..2432f69 100644 --- a/docs/ch13-01-closures.html +++ b/docs/ch13-01-closures.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-02-iterators.html b/docs/ch13-02-iterators.html index 56aaf7b..d673660 100644 --- a/docs/ch13-02-iterators.html +++ b/docs/ch13-02-iterators.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-03-improving-our-io-project.html b/docs/ch13-03-improving-our-io-project.html index b350b3e..67e9d0e 100644 --- a/docs/ch13-03-improving-our-io-project.html +++ b/docs/ch13-03-improving-our-io-project.html @@ -47,7 +47,7 @@
diff --git a/docs/ch13-04-performance.html b/docs/ch13-04-performance.html index 25aef63..0eab20c 100644 --- a/docs/ch13-04-performance.html +++ b/docs/ch13-04-performance.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-00-more-about-cargo.html b/docs/ch14-00-more-about-cargo.html index 866c169..32f65f8 100644 --- a/docs/ch14-00-more-about-cargo.html +++ b/docs/ch14-00-more-about-cargo.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-01-release-profiles.html b/docs/ch14-01-release-profiles.html index 1b0898c..eb6aa6e 100644 --- a/docs/ch14-01-release-profiles.html +++ b/docs/ch14-01-release-profiles.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-02-publishing-to-crates-io.html b/docs/ch14-02-publishing-to-crates-io.html index ba7da15..ce42c57 100644 --- a/docs/ch14-02-publishing-to-crates-io.html +++ b/docs/ch14-02-publishing-to-crates-io.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-03-cargo-workspaces.html b/docs/ch14-03-cargo-workspaces.html index f01980e..376bed9 100644 --- a/docs/ch14-03-cargo-workspaces.html +++ b/docs/ch14-03-cargo-workspaces.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-04-installing-binaries.html b/docs/ch14-04-installing-binaries.html index 10cb60f..c6153f3 100644 --- a/docs/ch14-04-installing-binaries.html +++ b/docs/ch14-04-installing-binaries.html @@ -47,7 +47,7 @@
diff --git a/docs/ch14-05-extending-cargo.html b/docs/ch14-05-extending-cargo.html index 1309062..7632800 100644 --- a/docs/ch14-05-extending-cargo.html +++ b/docs/ch14-05-extending-cargo.html @@ -47,7 +47,7 @@
@@ -87,6 +87,10 @@ commit 4f2dc564851dc04b271a2260c834643dfd86c724

+ +
@@ -97,6 +101,10 @@ commit 4f2dc564851dc04b271a2260c834643dfd86c724

+ +
diff --git a/docs/ch15-00-smart-pointers.html b/docs/ch15-00-smart-pointers.html new file mode 100644 index 0000000..dd96989 --- /dev/null +++ b/docs/ch15-00-smart-pointers.html @@ -0,0 +1,136 @@ + + + + + 智能指针 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+

智能指针

+
+

ch15-00-smart-pointers.md +
+commit 4f2dc564851dc04b271a2260c834643dfd86c724

+
+

指针是一个常见的编程概念,它代表一个指向储存其他数据的位置。第四章学习了 Rust 的引用;他们是一类很平常的指针,以&符号为标志并借用了他们所指向的值。智能指针Smart pointers)是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和能力,比如说引用计数。智能指针模式起源于 C++。在 Rust 中,普通引用和智能指针的一个额外的区别是引用是一类只借用数据的指针;相反大部分情况,智能指针拥有他们指向的数据。

+

本书中已经出现过一些智能指针,虽然当时我们并不这么称呼他们。例如在某种意义上说,第八章的StringVec<T>都是智能指针。他们拥有一些数据并允许你修改他们,并带有元数据(比如他们的容量)和额外的功能或保证(String的数据总是有效的 UTF-8 编码)。智能指针区别于常规结构体的特性在于他们实现了DerefDrop trait,而本章会讨论这些 trait 以及为什么对于智能指针来说他们很重要。

+

考虑到智能指针是一个在 Rust 经常被使用的通用设计模式,本章并不会覆盖所有现存的智能指针。很多库都有自己的智能指针而你也可以编写属于你自己的。这里将会讲到的是来自标准库中最常用的一些:

+
    +
  • Box<T>,用于在堆上分配值
  • +
  • Rc<T>,一个引用计数类型,其数据可以有多个所有者
  • +
  • RefCell<T>,其本身并不是只能指针,不过它管理智能指针RefRefMut的访问,在运行时而不是在编译时执行借用规则。
  • +
+

同时我们还将涉及:

+
    +
  • 内部可变性interior mutability)模式,当一个不可变类型暴露出改变其内部值的 API,这时借用规则适用于运行时而不是编译时。
  • +
  • 引用循环,它如何会泄露内存,以及如何避免他们
  • +
+

让我们开始吧!

+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch15-01-box.html b/docs/ch15-01-box.html new file mode 100644 index 0000000..527b791 --- /dev/null +++ b/docs/ch15-01-box.html @@ -0,0 +1,214 @@ + + + + + `Box<T>`用于已知大小的堆上数据 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+

Box<T>用于已知大小的堆上数据

+
+

ch15-01-box.md +
+commit 85b2c9ac704c9dc4bbedb97209d336afb9809dc1

+
+

最简单直接的智能指针是 box,它的类型是Box<T>。 box 允许你将一个单独的值放在堆上(第四章介绍或栈与堆)。列表 15-1 展示了如何使用 box 在堆上储存一个i32

+

Filename: src/main.rs

+
fn main() {
+    let b = Box::new(5);
+    println!("b = {}", b);
+}
+
+

Listing 15-1: Storing an i32 value on the heap using a +box

+

这会打印出b = 5。在这个例子中,我们可以像数据是储存在栈上的那样访问 box 中的数据。正如任何拥有数据所有权的值那样,当像b这样的 box 在main的末尾离开作用域时,它将被释放。这个释放过程作用于 box 本身(位于栈上)和它所指向的数据(位于堆上)。

+

将一个单独的值存放在堆上并不是很有意义,所以像列表 15-1 这样单独使用 box 并不常见。一个 box 的实用场景是当你希望确保类型有一个已知大小的时候。例如,考虑一下列表 15-2,它是一个用于 cons list 的枚举定义,这是一个来源于函数式编程的数据结构类型。注意它还不能编译:

+

Filename: src/main.rs

+
enum List {
+    Cons(i32, List),
+    Nil,
+}
+
+

Listing 15-2: The first attempt of defining an enum to +represent a cons list data structure of i32 values

+

我们实现了一个只存放i32值的 cons list。也可以选择实用第十章介绍的泛型来实现一个类型无关的 cons list。

+
+

cons list 的更多内容

+

cons list 是一个来源于 Lisp 编程语言及其方言的数据结构。在 Lisp 中,cons函数("construct function"的缩写)利用两个参数来构造一个新的列表,他们通常是一个单独的值和另一个列表。

+

cons 函数的概念涉及到更通用的函数式编程术语;“将 x 与 y 连接”通常意味着构建一个新的容器而将 x 的元素放在新容器的开头,其后则是容器 y 的元素。

+

cons list 通过递归调用cons函数产生。代表递归的 base case 的规范名称是Nil,它宣布列表的终止。注意这不同于第六章中的"null"或"nil"的概念,他们代表无效或缺失的值。

+
+

cons list 是一个每个元素和之后的其余部分都只包含一个值的列表。列表的其余部分由嵌套的 cons list 定义。其结尾由值Nil表示。cons list 在 Rust 中并不常见;通常Vec<T>是一个更好的选择。实现这个数据结构是Box<T>实用性的一个好的例子。让我们看看为什么!

+

使用 cons list 来储存列表1, 2, 3将看起来像这样:

+
use List::{Cons, Nil};
+
+fn main() {
+    let list = Cons(1, Cons(2, Cons(3, Nil)));
+}
+
+

第一个Cons储存了1和另一个List值。这个List是另一个包含2Cons值和下一个List值。这又是另一个存放了3Cons值和最后一个值为NilList,非递归成员代表了列表的结尾。

+

如果尝试编译上面的代码,会得到如列表 15-3 所示的错误:

+
error[E0072]: recursive type `List` has infinite size
+ -->
+  |
+1 |   enum List {
+  |  _^ starting here...
+2 | |     Cons(i32, List),
+3 | |     Nil,
+4 | | }
+  | |_^ ...ending here: recursive type has infinite size
+  |
+  = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to
+  make `List` representable
+
+

Listing 15-3: The error we get when attempting to define +a recursive enum

+

错误表明这个类型“有无限的大小”。为什么呢?因为List的一个成员被定义为递归的:它存放了另一个相同类型的值。这意味着 Rust 无法计算为了存放List值到底需要多少空间。让我们一点一点的看:首先了解一下 Rust 如何决定需要多少空间来存放一个非递归类型。回忆一下第六章讨论枚举定义时的列表 6-2 中定义的Message枚举:

+
enum Message {
+    Quit,
+    Move { x: i32, y: i32 },
+    Write(String),
+    ChangeColor(i32, i32, i32),
+}
+
+

当 Rust 需要知道需要为Message值分配多少空间时,它可以检查每一个成员并发现Message::Quit并不需要任何空间,Message::Move需要足够储存两个i32值的空间,依此类推。因此,Message值所需的最大空间等于储存其最大成员的空间大小。

+

与此相对当 Rust 编译器检查像列表 15-2 中的List这样的递归类型时会发生什么呢。编译器尝试计算出储存一个List枚举需要多少内存,并开始检查Cons成员,那么Cons需要的空间等于i32的大小加上List的大小。为了计算List需要多少内存,它检查其成员,从Cons成员开始。Cons成员储存了一个i32值和一个List值,这样的计算将无限进行下去,如图 15-4 所示:

+

An infinite Cons list

+

Figure 15-4: An infinite List consisting of infinite +Cons variants

+

Rust 无法计算出要为定义为递归的类型分配多少空间,所以编译器给出了列表 15-3 中的错误。这个错误也包括了有用的建议:

+
= help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to
+        make `List` representable
+
+

因为Box<T>是一个指针,我们总是知道它需要多少空间:指针需要一个usize大小的空间。这个usize的值将是堆数据的地址。而堆数据可以是任意大小,不过开始这个堆数据的地址总是能放进一个usize中。所以如果将列表 15-2 的定义修改为像这里列表 15-5 中的定义,并修改main函数为Cons成员中的值使用Box::new

+

Filename: src/main.rs

+
enum List {
+    Cons(i32, Box<List>),
+    Nil,
+}
+
+use List::{Cons, Nil};
+
+fn main() {
+    let list = Cons(1,
+        Box::new(Cons(2,
+            Box::new(Cons(3,
+                Box::new(Nil))))));
+}
+
+

Listing 15-5: Definition of List that uses Box<T> in +order to have a known size

+

这样编译器就能够计算出储存一个List值需要的大小了。Rust 将会检查List,同样的从Cons成员开始检查。Cons成员需要i32的大小加上一个usize的大小,因为 box 总是usize大小的,不管它指向的是什么。接着 Rust 检查Nil成员,它并储存一个值,所以Nil并不需要任何空间。我们通过 box 打破了这无限递归的连锁。图 15-6 展示了现在Cons成员看起来像什么:

+

A finite Cons list

+

Figure 15-6: A List that is not infinitely sized since +Cons holds a Box

+

这就是 box 主要应用场景:打破无限循环的数据结构以便编译器可以知道其大小。第十七章讨论 trait 对象时我们将了解另一个 Rust 中会出现未知大小数据的情况。

+

虽然我们并不经常使用 box,他们也是一个了解智能指针模式的好的方式。Box<T>作为智能指针经常被使用的两个方面是他们DerefDrop trait 的实现。让我们研究这些 trait 如何工作以及智能指针如何利用他们。

+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch15-02-deref.html b/docs/ch15-02-deref.html new file mode 100644 index 0000000..09ced3d --- /dev/null +++ b/docs/ch15-02-deref.html @@ -0,0 +1,134 @@ + + + + + `Deref` Trait 允许通过引用访问数据 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+

Deref Trait 允许通过引用访问数据

+
+

ch15-02-deref.md +
+commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56

+
+

第一个智能指针相关的重要 trait 是Deref,它允许我们重载*,解引用运算符(不同于乘法运算符和全局引用运算符)。重载智能指针的*方便访问其后的数据,在这个部分的稍后介绍解引用强制多态时我们会讨论方便的意义。

+

第八章的哈希 map 的“根据旧值更新一个值”部分简要的提到了解引用运算符。当时有一个可变引用,而我们希望改变这个引用所指向的值。为此,首先我们必须解引用。这是另一个使用i32值引用的例子:

+
let mut x = 5;
+{
+    let y = &mut x;
+
+    *y += 1
+}
+
+assert_eq!(6, x);
+
+

我们使用*y来访问可变引用y所指向的数据,

+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch15-03-drop.html b/docs/ch15-03-drop.html new file mode 100644 index 0000000..fe15cc2 --- /dev/null +++ b/docs/ch15-03-drop.html @@ -0,0 +1,116 @@ + + + + + `Drop` Trait 运行清理代码 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch15-04-rc.html b/docs/ch15-04-rc.html new file mode 100644 index 0000000..7ae633c --- /dev/null +++ b/docs/ch15-04-rc.html @@ -0,0 +1,116 @@ + + + + + `Rc<T>` 引用计数智能指针 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch15-05-interior-mutability.html b/docs/ch15-05-interior-mutability.html new file mode 100644 index 0000000..baff764 --- /dev/null +++ b/docs/ch15-05-interior-mutability.html @@ -0,0 +1,116 @@ + + + + + `RefCell<T>`和内部可变性模式 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch15-06-reference-cycles.html b/docs/ch15-06-reference-cycles.html new file mode 100644 index 0000000..cda577a --- /dev/null +++ b/docs/ch15-06-reference-cycles.html @@ -0,0 +1,108 @@ + + + + + 引用循环和内存泄漏是安全的 - Rust 程序设计语言 简体中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/img/trpl15-01.svg b/docs/img/trpl15-01.svg new file mode 100644 index 0000000..b873825 --- /dev/null +++ b/docs/img/trpl15-01.svg @@ -0,0 +1,42 @@ + + + + + + +%3 + + +table0 + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + + + + diff --git a/docs/img/trpl15-02.svg b/docs/img/trpl15-02.svg new file mode 100644 index 0000000..5b86985 --- /dev/null +++ b/docs/img/trpl15-02.svg @@ -0,0 +1,25 @@ + + + + + + +%3 + + +table0 + +Cons + +i32 + + +Box + +usize + + + diff --git a/docs/img/trpl15-03.svg b/docs/img/trpl15-03.svg new file mode 100644 index 0000000..e26024f --- /dev/null +++ b/docs/img/trpl15-03.svg @@ -0,0 +1,94 @@ + + + + + + +%3 + + +table4 +b + + +table5 + +3 + +   + + +table4:ptr4:c->table5:pte4 + + + + +table1 + +5 + +   + + +table5:ptr5:c->table1:pte0 + + + + +table0 +a + + +table0:ptr0:c->table1:pte0 + + + + +table2 + +10 + +   + + +table1:ptr1:c->table2:pte1 + + + + +table3 + +Nil + + +table2:ptr2:c->table3:pte2 + + + + +table6 +c + + +table7 + +4 + +   + + +table6:ptr6:c->table7:pte6 + + + + +table7:ptr7:c->table1:pte0 + + + + + diff --git a/docs/img/trpl15-04.svg b/docs/img/trpl15-04.svg new file mode 100644 index 0000000..96ad98c --- /dev/null +++ b/docs/img/trpl15-04.svg @@ -0,0 +1,55 @@ + + + + + + +%3 + + +table0 + +a + + +table1 + +5 + + + + +table2 + +b + + +table3 + +10 + + + + +table0:ref->table1:data + + + + +table1:ref->table2:data + + + + +table2:ref->table3:data + + + + +table3:ref->table0:data + + + + + diff --git a/docs/index.html b/docs/index.html index 2bf82a1..e6df29c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -46,7 +46,7 @@
diff --git a/docs/print.html b/docs/print.html index 3c98186..b3d634b 100644 --- a/docs/print.html +++ b/docs/print.html @@ -2,7 +2,7 @@ - Cargo 自定义扩展命令 - Rust 程序设计语言 简体中文版 + 引用循环和内存泄漏是安全的 - Rust 程序设计语言 简体中文版 @@ -47,7 +47,7 @@
@@ -8156,6 +8156,142 @@ commit 4f2dc564851dc04b271a2260c834643dfd86c724

Cargo 被设计为可扩展的,通过新的子命令而无须修改 Cargo 自身。如果$PATH中有类似cargo-something的二进制文件,就可以通过cargo something来像 Cargo 子命令一样运行它。像这样的自定义命令也可以运行cargo --list来展示出来,通过cargo install向 Cargo 安装扩展并可以如内建 Cargo 工具那样运行他们是很方便的!

总结

通过 Cargo 和 crates.io 来分享代码是使得 Rust 生态环境可以用于许多不同的任务的重要组成部分。Rust 的标准库是小而稳定的,不过 crate 易于分享和使用,并采用一个不同语言自身的时间线来提供改进。不要羞于在 crates.io 上共享对你有用的代码;因为它很有可能对别人也很有用!

+

智能指针

+
+

ch15-00-smart-pointers.md +
+commit 4f2dc564851dc04b271a2260c834643dfd86c724

+
+

指针是一个常见的编程概念,它代表一个指向储存其他数据的位置。第四章学习了 Rust 的引用;他们是一类很平常的指针,以&符号为标志并借用了他们所指向的值。智能指针Smart pointers)是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和能力,比如说引用计数。智能指针模式起源于 C++。在 Rust 中,普通引用和智能指针的一个额外的区别是引用是一类只借用数据的指针;相反大部分情况,智能指针拥有他们指向的数据。

+

本书中已经出现过一些智能指针,虽然当时我们并不这么称呼他们。例如在某种意义上说,第八章的StringVec<T>都是智能指针。他们拥有一些数据并允许你修改他们,并带有元数据(比如他们的容量)和额外的功能或保证(String的数据总是有效的 UTF-8 编码)。智能指针区别于常规结构体的特性在于他们实现了DerefDrop trait,而本章会讨论这些 trait 以及为什么对于智能指针来说他们很重要。

+

考虑到智能指针是一个在 Rust 经常被使用的通用设计模式,本章并不会覆盖所有现存的智能指针。很多库都有自己的智能指针而你也可以编写属于你自己的。这里将会讲到的是来自标准库中最常用的一些:

+
    +
  • Box<T>,用于在堆上分配值
  • +
  • Rc<T>,一个引用计数类型,其数据可以有多个所有者
  • +
  • RefCell<T>,其本身并不是只能指针,不过它管理智能指针RefRefMut的访问,在运行时而不是在编译时执行借用规则。
  • +
+

同时我们还将涉及:

+
    +
  • 内部可变性interior mutability)模式,当一个不可变类型暴露出改变其内部值的 API,这时借用规则适用于运行时而不是编译时。
  • +
  • 引用循环,它如何会泄露内存,以及如何避免他们
  • +
+

让我们开始吧!

+

Box<T>用于已知大小的堆上数据

+
+

ch15-01-box.md +
+commit 85b2c9ac704c9dc4bbedb97209d336afb9809dc1

+
+

最简单直接的智能指针是 box,它的类型是Box<T>。 box 允许你将一个单独的值放在堆上(第四章介绍或栈与堆)。列表 15-1 展示了如何使用 box 在堆上储存一个i32

+

Filename: src/main.rs

+
fn main() {
+    let b = Box::new(5);
+    println!("b = {}", b);
+}
+
+

Listing 15-1: Storing an i32 value on the heap using a +box

+

这会打印出b = 5。在这个例子中,我们可以像数据是储存在栈上的那样访问 box 中的数据。正如任何拥有数据所有权的值那样,当像b这样的 box 在main的末尾离开作用域时,它将被释放。这个释放过程作用于 box 本身(位于栈上)和它所指向的数据(位于堆上)。

+

将一个单独的值存放在堆上并不是很有意义,所以像列表 15-1 这样单独使用 box 并不常见。一个 box 的实用场景是当你希望确保类型有一个已知大小的时候。例如,考虑一下列表 15-2,它是一个用于 cons list 的枚举定义,这是一个来源于函数式编程的数据结构类型。注意它还不能编译:

+

Filename: src/main.rs

+
enum List {
+    Cons(i32, List),
+    Nil,
+}
+
+

Listing 15-2: The first attempt of defining an enum to +represent a cons list data structure of i32 values

+

我们实现了一个只存放i32值的 cons list。也可以选择实用第十章介绍的泛型来实现一个类型无关的 cons list。

+
+

cons list 的更多内容

+

cons list 是一个来源于 Lisp 编程语言及其方言的数据结构。在 Lisp 中,cons函数("construct function"的缩写)利用两个参数来构造一个新的列表,他们通常是一个单独的值和另一个列表。

+

cons 函数的概念涉及到更通用的函数式编程术语;“将 x 与 y 连接”通常意味着构建一个新的容器而将 x 的元素放在新容器的开头,其后则是容器 y 的元素。

+

cons list 通过递归调用cons函数产生。代表递归的 base case 的规范名称是Nil,它宣布列表的终止。注意这不同于第六章中的"null"或"nil"的概念,他们代表无效或缺失的值。

+
+

cons list 是一个每个元素和之后的其余部分都只包含一个值的列表。列表的其余部分由嵌套的 cons list 定义。其结尾由值Nil表示。cons list 在 Rust 中并不常见;通常Vec<T>是一个更好的选择。实现这个数据结构是Box<T>实用性的一个好的例子。让我们看看为什么!

+

使用 cons list 来储存列表1, 2, 3将看起来像这样:

+
use List::{Cons, Nil};
+
+fn main() {
+    let list = Cons(1, Cons(2, Cons(3, Nil)));
+}
+
+

第一个Cons储存了1和另一个List值。这个List是另一个包含2Cons值和下一个List值。这又是另一个存放了3Cons值和最后一个值为NilList,非递归成员代表了列表的结尾。

+

如果尝试编译上面的代码,会得到如列表 15-3 所示的错误:

+
error[E0072]: recursive type `List` has infinite size
+ -->
+  |
+1 |   enum List {
+  |  _^ starting here...
+2 | |     Cons(i32, List),
+3 | |     Nil,
+4 | | }
+  | |_^ ...ending here: recursive type has infinite size
+  |
+  = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to
+  make `List` representable
+
+

Listing 15-3: The error we get when attempting to define +a recursive enum

+

错误表明这个类型“有无限的大小”。为什么呢?因为List的一个成员被定义为递归的:它存放了另一个相同类型的值。这意味着 Rust 无法计算为了存放List值到底需要多少空间。让我们一点一点的看:首先了解一下 Rust 如何决定需要多少空间来存放一个非递归类型。回忆一下第六章讨论枚举定义时的列表 6-2 中定义的Message枚举:

+
enum Message {
+    Quit,
+    Move { x: i32, y: i32 },
+    Write(String),
+    ChangeColor(i32, i32, i32),
+}
+
+

当 Rust 需要知道需要为Message值分配多少空间时,它可以检查每一个成员并发现Message::Quit并不需要任何空间,Message::Move需要足够储存两个i32值的空间,依此类推。因此,Message值所需的最大空间等于储存其最大成员的空间大小。

+

与此相对当 Rust 编译器检查像列表 15-2 中的List这样的递归类型时会发生什么呢。编译器尝试计算出储存一个List枚举需要多少内存,并开始检查Cons成员,那么Cons需要的空间等于i32的大小加上List的大小。为了计算List需要多少内存,它检查其成员,从Cons成员开始。Cons成员储存了一个i32值和一个List值,这样的计算将无限进行下去,如图 15-4 所示:

+

An infinite Cons list

+

Figure 15-4: An infinite List consisting of infinite +Cons variants

+

Rust 无法计算出要为定义为递归的类型分配多少空间,所以编译器给出了列表 15-3 中的错误。这个错误也包括了有用的建议:

+
= help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to
+        make `List` representable
+
+

因为Box<T>是一个指针,我们总是知道它需要多少空间:指针需要一个usize大小的空间。这个usize的值将是堆数据的地址。而堆数据可以是任意大小,不过开始这个堆数据的地址总是能放进一个usize中。所以如果将列表 15-2 的定义修改为像这里列表 15-5 中的定义,并修改main函数为Cons成员中的值使用Box::new

+

Filename: src/main.rs

+
enum List {
+    Cons(i32, Box<List>),
+    Nil,
+}
+
+use List::{Cons, Nil};
+
+fn main() {
+    let list = Cons(1,
+        Box::new(Cons(2,
+            Box::new(Cons(3,
+                Box::new(Nil))))));
+}
+
+

Listing 15-5: Definition of List that uses Box<T> in +order to have a known size

+

这样编译器就能够计算出储存一个List值需要的大小了。Rust 将会检查List,同样的从Cons成员开始检查。Cons成员需要i32的大小加上一个usize的大小,因为 box 总是usize大小的,不管它指向的是什么。接着 Rust 检查Nil成员,它并储存一个值,所以Nil并不需要任何空间。我们通过 box 打破了这无限递归的连锁。图 15-6 展示了现在Cons成员看起来像什么:

+

A finite Cons list

+

Figure 15-6: A List that is not infinitely sized since +Cons holds a Box

+

这就是 box 主要应用场景:打破无限循环的数据结构以便编译器可以知道其大小。第十七章讨论 trait 对象时我们将了解另一个 Rust 中会出现未知大小数据的情况。

+

虽然我们并不经常使用 box,他们也是一个了解智能指针模式的好的方式。Box<T>作为智能指针经常被使用的两个方面是他们DerefDrop trait 的实现。让我们研究这些 trait 如何工作以及智能指针如何利用他们。

+

Deref Trait 允许通过引用访问数据

+
+

ch15-02-deref.md +
+commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56

+
+

第一个智能指针相关的重要 trait 是Deref,它允许我们重载*,解引用运算符(不同于乘法运算符和全局引用运算符)。重载智能指针的*方便访问其后的数据,在这个部分的稍后介绍解引用强制多态时我们会讨论方便的意义。

+

第八章的哈希 map 的“根据旧值更新一个值”部分简要的提到了解引用运算符。当时有一个可变引用,而我们希望改变这个引用所指向的值。为此,首先我们必须解引用。这是另一个使用i32值引用的例子:

+
let mut x = 5;
+{
+    let y = &mut x;
+
+    *y += 1
+}
+
+assert_eq!(6, x);
+
+

我们使用*y来访问可变引用y所指向的数据,

diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 96f1381..620a10e 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -76,4 +76,12 @@ - [将 crate 发布到 Crates.io](ch14-02-publishing-to-crates-io.md) - [Cargo 工作空间](ch14-03-cargo-workspaces.md) - [使用`cargo install`从 Crates.io 安装文件](ch14-04-installing-binaries.md) - - [Cargo 自定义扩展命令](ch14-05-extending-cargo.md) \ No newline at end of file + - [Cargo 自定义扩展命令](ch14-05-extending-cargo.md) + +- [智能指针](ch15-00-smart-pointers.md) + - [`Box`用于已知大小的堆上数据](ch15-01-box.md) + - [`Deref` Trait 允许通过引用访问数据](ch15-02-deref.md) + - [`Drop` Trait 运行清理代码](ch15-03-drop.md) + - [`Rc` 引用计数智能指针](ch15-04-rc.md) + - [`RefCell`和内部可变性模式](ch15-05-interior-mutability.md) + - [引用循环和内存泄漏是安全的](ch15-06-reference-cycles.md) \ No newline at end of file diff --git a/src/ch15-00-smart-pointers.md b/src/ch15-00-smart-pointers.md new file mode 100644 index 0000000..c55e9b5 --- /dev/null +++ b/src/ch15-00-smart-pointers.md @@ -0,0 +1,22 @@ +# 智能指针 + +> [ch15-00-smart-pointers.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch15-00-smart-pointers.md) +>
+> commit 4f2dc564851dc04b271a2260c834643dfd86c724 + +**指针**是一个常见的编程概念,它代表一个指向储存其他数据的位置。第四章学习了 Rust 的引用;他们是一类很平常的指针,以`&`符号为标志并借用了他们所指向的值。**智能指针**(*Smart pointers*)是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和能力,比如说引用计数。智能指针模式起源于 C++。在 Rust 中,普通引用和智能指针的一个额外的区别是引用是一类只借用数据的指针;相反大部分情况,智能指针**拥有**他们指向的数据。 + +本书中已经出现过一些智能指针,虽然当时我们并不这么称呼他们。例如在某种意义上说,第八章的`String`和`Vec`都是智能指针。他们拥有一些数据并允许你修改他们,并带有元数据(比如他们的容量)和额外的功能或保证(`String`的数据总是有效的 UTF-8 编码)。智能指针区别于常规结构体的特性在于他们实现了`Deref`和`Drop` trait,而本章会讨论这些 trait 以及为什么对于智能指针来说他们很重要。 + +考虑到智能指针是一个在 Rust 经常被使用的通用设计模式,本章并不会覆盖所有现存的智能指针。很多库都有自己的智能指针而你也可以编写属于你自己的。这里将会讲到的是来自标准库中最常用的一些: + +* `Box`,用于在堆上分配值 +* `Rc`,一个引用计数类型,其数据可以有多个所有者 +* `RefCell`,其本身并不是只能指针,不过它管理智能指针`Ref`和`RefMut`的访问,在运行时而不是在编译时执行借用规则。 + +同时我们还将涉及: + +* **内部可变性**(*interior mutability*)模式,当一个不可变类型暴露出改变其内部值的 API,这时借用规则适用于运行时而不是编译时。 +* 引用循环,它如何会泄露内存,以及如何避免他们 + +让我们开始吧! \ No newline at end of file diff --git a/src/ch15-01-box.md b/src/ch15-01-box.md new file mode 100644 index 0000000..68ff9d1 --- /dev/null +++ b/src/ch15-01-box.md @@ -0,0 +1,140 @@ +## `Box`用于已知大小的堆上数据 + +> [ch15-01-box.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch15-01-box.md) +>
+> commit 85b2c9ac704c9dc4bbedb97209d336afb9809dc1 + +最简单直接的智能指针是 *box*,它的类型是`Box`。 box 允许你将一个单独的值放在堆上(第四章介绍或栈与堆)。列表 15-1 展示了如何使用 box 在堆上储存一个`i32`: + +Filename: src/main.rs + +```rust +fn main() { + let b = Box::new(5); + println!("b = {}", b); +} +``` + +Listing 15-1: Storing an `i32` value on the heap using a +box + +这会打印出`b = 5`。在这个例子中,我们可以像数据是储存在栈上的那样访问 box 中的数据。正如任何拥有数据所有权的值那样,当像`b`这样的 box 在`main`的末尾离开作用域时,它将被释放。这个释放过程作用于 box 本身(位于栈上)和它所指向的数据(位于堆上)。 + +将一个单独的值存放在堆上并不是很有意义,所以像列表 15-1 这样单独使用 box 并不常见。一个 box 的实用场景是当你希望确保类型有一个已知大小的时候。例如,考虑一下列表 15-2,它是一个用于 *cons list* 的枚举定义,这是一个来源于函数式编程的数据结构类型。注意它还不能编译: + +Filename: src/main.rs + +```rust,ignore +enum List { + Cons(i32, List), + Nil, +} +``` + +Listing 15-2: The first attempt of defining an enum to +represent a cons list data structure of `i32` values + +我们实现了一个只存放`i32`值的 cons list。也可以选择实用第十章介绍的泛型来实现一个类型无关的 cons list。 + +> #### cons list 的更多内容 +> +> *cons list* 是一个来源于 Lisp 编程语言及其方言的数据结构。在 Lisp 中,`cons`函数("construct function"的缩写)利用两个参数来构造一个新的列表,他们通常是一个单独的值和另一个列表。 +> +> cons 函数的概念涉及到更通用的函数式编程术语;“将 x 与 y 连接”通常意味着构建一个新的容器而将 x 的元素放在新容器的开头,其后则是容器 y 的元素。 +> +> cons list 通过递归调用`cons`函数产生。代表递归的 base case 的规范名称是`Nil`,它宣布列表的终止。注意这不同于第六章中的"null"或"nil"的概念,他们代表无效或缺失的值。 + +cons list 是一个每个元素和之后的其余部分都只包含一个值的列表。列表的其余部分由嵌套的 cons list 定义。其结尾由值`Nil`表示。cons list 在 Rust 中并不常见;通常`Vec`是一个更好的选择。实现这个数据结构是`Box`实用性的一个好的例子。让我们看看为什么! + +使用 cons list 来储存列表`1, 2, 3`将看起来像这样: + +```rust,ignore +use List::{Cons, Nil}; + +fn main() { + let list = Cons(1, Cons(2, Cons(3, Nil))); +} +``` + +第一个`Cons`储存了`1`和另一个`List`值。这个`List`是另一个包含`2`的`Cons`值和下一个`List`值。这又是另一个存放了`3`的`Cons`值和最后一个值为`Nil`的`List`,非递归成员代表了列表的结尾。 + +如果尝试编译上面的代码,会得到如列表 15-3 所示的错误: + +``` +error[E0072]: recursive type `List` has infinite size + --> + | +1 | enum List { + | _^ starting here... +2 | | Cons(i32, List), +3 | | Nil, +4 | | } + | |_^ ...ending here: recursive type has infinite size + | + = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to + make `List` representable +``` + +Listing 15-3: The error we get when attempting to define +a recursive enum + +错误表明这个类型“有无限的大小”。为什么呢?因为`List`的一个成员被定义为递归的:它存放了另一个相同类型的值。这意味着 Rust 无法计算为了存放`List`值到底需要多少空间。让我们一点一点的看:首先了解一下 Rust 如何决定需要多少空间来存放一个非递归类型。回忆一下第六章讨论枚举定义时的列表 6-2 中定义的`Message`枚举: + +```rust +enum Message { + Quit, + Move { x: i32, y: i32 }, + Write(String), + ChangeColor(i32, i32, i32), +} +``` + +当 Rust 需要知道需要为`Message`值分配多少空间时,它可以检查每一个成员并发现`Message::Quit`并不需要任何空间,`Message::Move`需要足够储存两个`i32`值的空间,依此类推。因此,`Message`值所需的最大空间等于储存其最大成员的空间大小。 + +与此相对当 Rust 编译器检查像列表 15-2 中的`List`这样的递归类型时会发生什么呢。编译器尝试计算出储存一个`List`枚举需要多少内存,并开始检查`Cons`成员,那么`Cons`需要的空间等于`i32`的大小加上`List`的大小。为了计算`List`需要多少内存,它检查其成员,从`Cons`成员开始。`Cons`成员储存了一个`i32`值和一个`List`值,这样的计算将无限进行下去,如图 15-4 所示: + +An infinite Cons list + +Figure 15-4: An infinite `List` consisting of infinite +`Cons` variants + +Rust 无法计算出要为定义为递归的类型分配多少空间,所以编译器给出了列表 15-3 中的错误。这个错误也包括了有用的建议: + +```text += help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to + make `List` representable +``` + +因为`Box`是一个指针,我们总是知道它需要多少空间:指针需要一个`usize`大小的空间。这个`usize`的值将是堆数据的地址。而堆数据可以是任意大小,不过开始这个堆数据的地址总是能放进一个`usize`中。所以如果将列表 15-2 的定义修改为像这里列表 15-5 中的定义,并修改`main`函数为`Cons`成员中的值使用`Box::new`: + +Filename: src/main.rs + +```rust +enum List { + Cons(i32, Box), + Nil, +} + +use List::{Cons, Nil}; + +fn main() { + let list = Cons(1, + Box::new(Cons(2, + Box::new(Cons(3, + Box::new(Nil)))))); +} +``` + +Listing 15-5: Definition of `List` that uses `Box` in +order to have a known size + +这样编译器就能够计算出储存一个`List`值需要的大小了。Rust 将会检查`List`,同样的从`Cons`成员开始检查。`Cons`成员需要`i32`的大小加上一个`usize`的大小,因为 box 总是`usize`大小的,不管它指向的是什么。接着 Rust 检查`Nil`成员,它并储存一个值,所以`Nil`并不需要任何空间。我们通过 box 打破了这无限递归的连锁。图 15-6 展示了现在`Cons`成员看起来像什么: + +A finite Cons list + +Figure 15-6: A `List` that is not infinitely sized since +`Cons` holds a `Box` + +这就是 box 主要应用场景:打破无限循环的数据结构以便编译器可以知道其大小。第十七章讨论 trait 对象时我们将了解另一个 Rust 中会出现未知大小数据的情况。 + +虽然我们并不经常使用 box,他们也是一个了解智能指针模式的好的方式。`Box`作为智能指针经常被使用的两个方面是他们`Deref`和`Drop` trait 的实现。让我们研究这些 trait 如何工作以及智能指针如何利用他们。 \ No newline at end of file diff --git a/src/ch15-02-deref.md b/src/ch15-02-deref.md new file mode 100644 index 0000000..9708c76 --- /dev/null +++ b/src/ch15-02-deref.md @@ -0,0 +1,22 @@ +## `Deref` Trait 允许通过引用访问数据 + +> [ch15-02-deref.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch15-02-deref.md) +>
+> commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56 + +第一个智能指针相关的重要 trait 是`Deref`,它允许我们重载`*`,解引用运算符(不同于乘法运算符和全局引用运算符)。重载智能指针的`*`方便访问其后的数据,在这个部分的稍后介绍解引用强制多态时我们会讨论方便的意义。 + +第八章的哈希 map 的“根据旧值更新一个值”部分简要的提到了解引用运算符。当时有一个可变引用,而我们希望改变这个引用所指向的值。为此,首先我们必须解引用。这是另一个使用`i32`值引用的例子: + +```rust +let mut x = 5; +{ + let y = &mut x; + + *y += 1 +} + +assert_eq!(6, x); +``` + +我们使用`*y`来访问可变引用`y`所指向的数据, \ No newline at end of file diff --git a/src/ch15-03-drop.md b/src/ch15-03-drop.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ch15-04-rc.md b/src/ch15-04-rc.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ch15-05-interior-mutability.md b/src/ch15-05-interior-mutability.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ch15-06-reference-cycles.md b/src/ch15-06-reference-cycles.md new file mode 100644 index 0000000..e69de29 diff --git a/src/img/trpl15-01.svg b/src/img/trpl15-01.svg new file mode 100644 index 0000000..b873825 --- /dev/null +++ b/src/img/trpl15-01.svg @@ -0,0 +1,42 @@ + + + + + + +%3 + + +table0 + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + +Cons + +i32 + + + + + diff --git a/src/img/trpl15-02.svg b/src/img/trpl15-02.svg new file mode 100644 index 0000000..5b86985 --- /dev/null +++ b/src/img/trpl15-02.svg @@ -0,0 +1,25 @@ + + + + + + +%3 + + +table0 + +Cons + +i32 + + +Box + +usize + + + diff --git a/src/img/trpl15-03.svg b/src/img/trpl15-03.svg new file mode 100644 index 0000000..e26024f --- /dev/null +++ b/src/img/trpl15-03.svg @@ -0,0 +1,94 @@ + + + + + + +%3 + + +table4 +b + + +table5 + +3 + +   + + +table4:ptr4:c->table5:pte4 + + + + +table1 + +5 + +   + + +table5:ptr5:c->table1:pte0 + + + + +table0 +a + + +table0:ptr0:c->table1:pte0 + + + + +table2 + +10 + +   + + +table1:ptr1:c->table2:pte1 + + + + +table3 + +Nil + + +table2:ptr2:c->table3:pte2 + + + + +table6 +c + + +table7 + +4 + +   + + +table6:ptr6:c->table7:pte6 + + + + +table7:ptr7:c->table1:pte0 + + + + + diff --git a/src/img/trpl15-04.svg b/src/img/trpl15-04.svg new file mode 100644 index 0000000..96ad98c --- /dev/null +++ b/src/img/trpl15-04.svg @@ -0,0 +1,55 @@ + + + + + + +%3 + + +table0 + +a + + +table1 + +5 + + + + +table2 + +b + + +table3 + +10 + + + + +table0:ref->table1:data + + + + +table1:ref->table2:data + + + + +table2:ref->table3:data + + + + +table3:ref->table0:data + + + + +