├── .gitignore ├── README.md ├── SUMMARY.md ├── book.json ├── fifth-basics.md ├── fifth-extras.md ├── fifth-final.md ├── fifth-layout.md ├── fifth-unsafe.md ├── fifth.md ├── first-drop.md ├── first-final.md ├── first-layout.md ├── first-new.md ├── first-ownership.md ├── first-pop.md ├── first-push.md ├── first-test.md ├── first.md ├── fourth-breaking.md ├── fourth-building.md ├── fourth-final.md ├── fourth-iteration.md ├── fourth-layout.md ├── fourth-peek.md ├── fourth-symmetry.md ├── fourth.md ├── indy.gif ├── infinity-double-single.md ├── infinity.md ├── second-final.md ├── second-generic.md ├── second-into-iter.md ├── second-iter-mut.md ├── second-iter.md ├── second-option.md ├── second-peek.md ├── second.md ├── sixth.md ├── styles ├── pdf.css └── website.css ├── third-arc.md ├── third-basics.md ├── third-drop.md ├── third-final.md ├── third-layout.md └── third.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > This is the Simplified Chinese translation of [Learn Rust by writing Entirely Too Many linked lists][original]. 2 | 3 | [GitBook在线阅读][gitbook] 4 | 5 | # 通过大量的链表学习Rust 6 | 7 | 有任何issue或者想要查看所有的最终代码?[它们都在Github上!][github] 8 | 9 | 我最近经常被问到如何在Rust里实现一个链表。这个问题的答案实际上取决于你的需求,且并不是很容易立刻回答的。因此,我决定写这本书来通过易懂的方式一劳永逸的回答这个问题。 10 | 11 | 在这一系列教程中,我将会仅通过让你实现6个链表,教会你基础和高级的Rust编程。通过这么做,你应该能够学会: 12 | 13 | * 几种指针类型: `&`, `&mut`, `Box`, `Rc`, `Arc`, `*const`, `*mut` 14 | * 所有权,借用,可变性继承,内部可变性,Copy 15 | * 所有的关键词:struct, enum, fn, pub, impl, use, ... 16 | * 模式匹配,泛型,析构器 17 | * 测试 18 | * 不安全Rust基础 19 | 20 | 没错,链表真的很糟糕,你必须用到所有这些概念才能够实现它。 21 | 22 | 作为快速参考,这是我们即将做的内容: 23 | 24 | 1. [一个糟糕的单向链表栈](first.md) 25 | 2. [一个还行的单向链表栈](second.md) 26 | 3. [一个固定的单向链表栈](third.md) 27 | 4. [一个很糟糕但是安全的双向链表Deque](fourth.md) 28 | 5. [一个不安全的单向链表队列](fifth.md) 29 | 6. [TODO:一个还行但是不安全的双向链表Deque](sixth.md) 30 | 7. [追加:一系列很蠢的列表](infinity.md) 31 | 32 | 就在这页,我会写出所有我们会在控制台调用的指令。我会使用Rust的标准包管理器,Cargo,来进行项目的开发。Cargo对于开发一个Rust程序并不是必要的,但是它比直接使用 `rustc` 要*好上太多*。如果你只想随便玩玩,也可以通过 https://play.rust-lang.org/ ,在浏览器里直接运行简单的程序,尽管它不支持单元测试。 33 | 34 | 让我们开始构建我们的项目吧: 35 | 36 | ```text 37 | > cargo new lists 38 | > cd lists 39 | ``` 40 | 41 | 我们会把每个列表放在不同的文件里,这样便不会丢失工作。 42 | 43 | 需要注意的是,*真正的*Rust学习体验涉及实际编写代码,让编译器对你大吼大叫,然后尝试去找出这到底意味着什么。我会小心的保证这件事经常发生。学会阅读并理解Rust的(通常)极好的编译错误以及代码文档对成为一个有创造力的Rust程序员是*极其*重要的。 44 | 45 | 上面说的其实是骗人的。在写书时我遇到了比我所写出来的*多得多*的错误。特别的,在之后的章节中我不会指出“我打错了XXX”这类所有语言中常见的错误。这本书是一个让编译器对你大吼大叫的*向导旅行*。 46 | 47 | 我们将以较慢的速度前行,而且我全程都不会怎么严肃起来。我觉得编程应该是非常欢乐的,让我们搞起!如果你是那种希望大信息量、严肃和正式的内容的人,这本书不是为你准备的。我做的东西没一个是为你准备的。你错了。 48 | 49 | 50 | 51 | # 义务公共服务声明 52 | 53 | 我们先100%的搞清楚这一点:我讨厌链表。沉痛的讨厌着。链表是极其糟糕的数据结构。好吧,当然这里有一些链表的重要用例: 54 | 55 | * 你想对大容量的列表进行*大量的*分隔或合并操作。*大量的*。 56 | * 你在做一些很牛逼的无锁并行计算之类的东西。 57 | * 你在写内核/嵌入式程序并且想要使用侵入式列表。 58 | * 你在使用一个纯函数式语言,链表的受限语法以及不可变性让它更容易被处理。 59 | * ... 还有更多! 60 | 61 | 但是,对于任何写Rust程序的人,以上的所有情况都是*极其稀有*的。99%的时候你应该只使用 Vec(数组栈),而剩下的1%内的99%的时候你应该使用一个 VecDeque (数组双向队列)。由于较少的内存分配、更低的内存负载、真正的随机访问和缓存局部性,它们在大多数工作条件下都显然是更好的数据结构。 62 | 63 | 链表是和字典树(trie)一样用途狭窄并且模糊不清的数据结构。有的人会回避我,说字典树是一个极其特定的数据结构,像你们这样的普通程序员即便整个编程生涯不学也可以过的很开心——而链表则有着显赫的名声。我们教给每个大学毕业生如何写链表。它是[我没办法从 std::collections 里干掉的唯一一个不泛用的数据结构][rust-std-list]。它也是[C++里的*那个*list][cpp-std-list]! 64 | 65 | 我们应该作为一个社群,对把链表当做一个“标准”数据结构的行为说*不*。它是一个有一些极好用途的还行的数据结构,但是这些用例是*特殊的*,不是通常的。 66 | 67 | 有些人显然只读了这段声明的第一段就停止了阅读。他们会尝试通过列出我所写的链表的重要用例中的一项来反驳我的言论。那东西可就在第一段后面啊! 68 | 69 | 我可以更详细的开始讨论这件事了,下面是一些我曾经见过的反论的尝试,以及我对它们的回应。如果你只是想学一些Rust的话,请随意跳转到[结尾](#喘口气)! 70 | 71 | 72 | 73 | ## 性能并不总是重要的 74 | 75 | 是的!也许你的应用程序是I/O密集的,或者要讨论的代码的运行频率太低以至于性能不重要。但是这甚至都不是使用链表的一个论据。这是使用*任何东西*的论据。为什么要使用一个链表呢?用一个链式哈希表(linked hash map)吧! 76 | 77 | 如果性能不重要,那选择采用数组的自然特性当然是可以的。 78 | 79 | 80 | 81 | ## 如果你拥有一个链表指针,那么分割-附加-插入-删除操作的时间复杂度都是O(1) 82 | 83 | 嗯哼!虽然[Bjarne Stroustrup所说][bjarne]的是正确的,但如果获取指针所用的时间远远超出了简单的拷贝数组内所有元素的时间(它真的很快),那*这其实根本不重要*。 84 | 85 | 除非你的程序负载完全由分割和合并操作的时间消耗所主导,其他*每一个操作*由于缓存不友好和代码复杂度带来的时间损失会消除任何理论上的好处。 86 | 87 | 但是没错,如果你对应用程序进行性能剖析(profiling),发现它花了大量的时间在分割和合并上,使用链表可能让你节省时间。 88 | 89 | 90 | 91 | 92 | 93 | ## 我没法忍受复杂度均摊(armortization) 94 | 95 | 你已经进入到一个十分狭窄的空间——大多数人都能忍受复杂度均摊。数组只在最坏情况下进行复杂度均摊。使用数组也并不意味着你一定要进行均摊。如果你可以预测有多少元素要存储(或者有一个上界),你可以预先分配好所有需要的空间。在我的经验里,能够预测需要的元素数量是非常常见的。对于Rust来说,所有的迭代器都为这种情况提供了一个 `size_hint`。 96 | 97 | 在这种情况下,`push`和`pop`就会真正成为 O(1) 操作。而且它们将会比在链表上的 `push`和 `pop` 高出一个数量级。你进行一次指针偏移,写入字节,递增一个整数。不需要访问任何的内存分配器。 98 | 99 | 如果你要求低延迟的话,这样不是更好么? 100 | 101 | *但是没错,如果你无法预测你的工作负载,那最坏情况下的延迟降低也要考虑在内!* 102 | 103 | 104 | 105 | 106 | 107 | ## 链表浪费的空间更少 108 | 109 | 呃,这东西比较复杂。一个“标准”的数组大小重分配策略会将数组增长或缩小,来保证最多只有一半空间是空的。这确实会很多浪费的空间。尤其是在Rust中,我们不会自动缩减集合的内存占用(如果你要把它填充回去,这只会造成浪费),因此浪费程度可以达到正无穷! 110 | 111 | 但这是最坏情况的状态。在最优情况下,一个数组栈管理整个数组只需要三个指针的额外开销——基本可以忽略。 112 | 113 | 而链表则对每个元素都无条件的浪费内存空间。一个单向链表的元素浪费了一个指针,而双向链表浪费两个。和数组不一样,链表的相对浪费量和元素数量呈正比。如果一个元素所占空间*非常巨大*,浪费会趋近于0。如果每个元素所占空间很小(例如,比特),这可能造成最多16倍的内存浪费(如果是32位,8倍)! 114 | 115 | 实际的数字应该更接近23倍(或者32位时的11倍),因为那一个字节需要进行位对齐,让整个节点的大小对齐到一个指针。 116 | 117 | 这也是在对内存分配器进行最优条件假设的前提下得出的结论:节点的内存分配和释放会紧密的进行,并且你不会因为内存碎片化而丢失空间。 118 | 119 | *但是没错,如果每个元素所占空间巨大,你无法预测负载,并且拥有一个高效的内存分配器,那这确实可以节省内存!* 120 | 121 | 122 | 123 | 124 | 125 | 126 | ## 我在 <键入函数式语言名称> 中一直使用链表 127 | 128 | 棒极了!在函数式语言中使用链表是非常优雅的,因为你可以在不涉及任何可变性的情况下操作它们,可以递归的描述它们,甚至可以借助惰性求值的魔法来操作无穷大列表。 129 | 130 | 特别的,链表因为无需任何可变状态就可以表示迭代而显得特别优雅。迭代的下一步就是访问下一个子列表而已。 131 | 132 | 不过应该注意的是,Rust可以对数组进行模式匹配,并且使用[切片][slices]来处理子数组!从某些角度来说,它甚至比一个函数式列表更富表达力,因为你可以专门处理最后一个元素或者甚至“没有第一个和最后两个元素的数组”或者任何你想要的疯狂的东西。 133 | 134 | 你不能通过切片来*构造*一个列表倒是真的。你只能把它们撕成小片。 135 | 136 | 对于惰性求值,我们有[迭代器][iterators]作为替代。它可以是无穷的,而你可以像对待一个函数式列表一样对它们 `map`, `filter`, `reverse` 和 `concatenate`,这些操作都会被惰性的执行。不必说,数组切片也可以被转换(coerce)成一个迭代器。 137 | 138 | *不过没错,如果你只限制于使用不可变的语义,链表是很好用的。* 139 | 140 | 注意我并没有说函数式编程一定是弱的或糟糕的。然而它*确实是*从根本上语义受限的:你很大程度上只被允许讨论事情*是怎么样*,而非它们应该如何被*完成*。这实际上是一个*特性*,因为它让编译器得以进行成吨的[诡异变换][ghc]来找出潜在的*最佳*工作方式而不需要你去担心它。然而,这也带来了*完全无法*去担心它的代价。通常的情况下可以找到应急出口,但到达某个限度后你又会开始写过程式的代码了。 141 | 142 | 即便在函数式语言中,你也应该在确实需要用到数据结构时选择恰当的数据结构。没错,链表是操作控制流的主要工具,但是如果要在里面存储一堆数据并且查询的话,它们是非常糟糕的。 143 | 144 | 145 | ## 在构建并行数据结构时,链表是很好用的! 146 | 147 | 没错!尽管如此,实现一个并行数据结构真的是另一个很大的话题,不应该被轻视。这显然都不是很多人会考虑去做的事。当它实际被实现以后,你也真的不会真的选择使用链表。你会选择使用一个MPSC队列或者其他什么东西。在这个情况下实现策略实际上已经在考虑范围之外了! 148 | 149 | *不过没错,链表是无锁并行开发的暗黑领域的绝对王者。* 150 | 151 | 152 | 153 | 154 | ## 呃。。内核。。嵌入式。。用了。。侵入式链表。。。 155 | 156 | 这很用途狭窄。你在讨论这个的时候甚至没有使用你所用语言的*运行时*。这难道不是你正在做一些奇怪的事的警告信号么? 157 | 158 | 这也是非常不安全(unsafe)的。 159 | 160 | *但是当然了。在栈上构建你的碉堡的零内存分配列表吧。* 161 | 162 | 163 | 164 | 165 | 166 | ## 无关的插入和删除不会让迭代器失效 167 | 168 | 你在跳一支危险的舞。尤其是在你没有垃圾收集器的时候。根据细节,我应该会争论说你的控制流和所有权模式恐怕有点纠缠的太紧了。 169 | 170 | *但是没错,你可以用cursor做一些非常酷的东西。* 171 | 172 | 173 | 174 | 175 | ## 它们很简单并且容易用于教学! 176 | 177 | 呃,是啊。你就在读一本以此作为前提写的书。 178 | 好吧,单向链表是相当简单的。双向链表则会变得比较麻烦,我们马上就会看到。 179 | 180 | 181 | # 喘口气 182 | 183 | Ok,这个问题解决了。让我们开始写一大堆链表吧。 184 | 185 | 186 | [rust-std-list]: https://doc.rust-lang.org/std/collections/struct.LinkedList.html 187 | [cpp-std-list]: http://en.cppreference.com/w/cpp/container/list 188 | [github]: https://github.com/Gankro/too-many-lists 189 | [bjarne]: https://www.youtube.com/watch?v=YQs6IC-vgmo 190 | [slices]: https://doc.rust-lang.org/book/slice-patterns.html 191 | [iterators]: https://doc.rust-lang.org/std/iter/trait.Iterator.html 192 | [ghc]: https://wiki.haskell.org/GHC_optimisations#Fusion 193 | 194 | 195 | [original]: https://github.com/rust-unofficial/too-many-lists 196 | [gitbook]: https://www.gitbook.com/read/book/weathfold/rust-too-many-lists-zhcn 197 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [序](README.md) 4 | * [一个糟糕的栈](first.md) 5 | * [布局](first-layout.md) 6 | * [创建](first-new.md) 7 | * [所有权入门](first-ownership.md) 8 | * [Push](first-push.md) 9 | * [Pop](first-pop.md) 10 | * [测试](first-test.md) 11 | * [Drop](first-drop.md) 12 | * [最终代码](first-final.md) 13 | * [一个还行的栈](second.md) 14 | * [Option](second-option.md) 15 | * [泛型](second-generic.md) 16 | * [Peek](second-peek.md) 17 | * [IntoIter](second-into-iter.md) 18 | * [Iter](second-iter.md) 19 | * [IterMut](second-iter-mut.md) 20 | * [最终代码](second-final.md) 21 | * [一个不可变的栈](third.md) 22 | * [布局](third-layout.md) 23 | * [基本](third-basics.md) 24 | * [Drop](third-drop.md) 25 | * [Arc](third-arc.md) 26 | * [最终代码](third-final.md) 27 | * [一个糟糕但安全的双向队列](fourth.md) 28 | * [布局](fourth-layout.md) 29 | * [构建](fourth-building.md) 30 | * [任务分解](fourth-breaking.md) 31 | * [Peek](fourth-peek.md) 32 | * [对称情况](fourth-symmetry.md) 33 | * [迭代器](fourth-iteration.md) 34 | * [最终代码](fourth-final.md) 35 | * [一个不安全的队列](fifth.md) 36 | * [布局](fifth-layout.md) 37 | * [Unsafe](fifth-unsafe.md) 38 | * [基础内容](fifth-basics.md) 39 | * [附加内容](fifth-extras.md) 40 | * [最终代码](fifth-final.md) 41 | * [一个还行但不安全的双向队列](sixth.md) 42 | * [各种愚蠢的列表](infinity.md) 43 | * [The Double Single](infinity-double-single.md) 44 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "zh-cn" 3 | } -------------------------------------------------------------------------------- /fifth-basics.md: -------------------------------------------------------------------------------- 1 | % Basics 2 | 3 | Alright, back to basics. How do we construct our list? 4 | 5 | Before we just did: 6 | 7 | ```rust 8 | impl List { 9 | pub fn new() -> Self { 10 | List { head: None, tail: None } 11 | } 12 | } 13 | ``` 14 | 15 | But we're not using Option for the `tail` anymore: 16 | 17 | ```text 18 | > cargo build 19 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 20 | src/fifth.rs:15:34: 15:38 error: mismatched types: 21 | expected `*mut fifth::Node<_>`, 22 | found `core::option::Option<_>` 23 | (expected *-ptr, 24 | found enum `core::option::Option`) [E0308] 25 | src/fifth.rs:15 List { head: None, tail: None } 26 | ^~~~ 27 | src/fifth.rs:15:34: 15:38 help: run `rustc --explain E0308` to see a detailed explanation 28 | error: aborting due to previous error 29 | ``` 30 | 31 | We *could* use an Option, but unlike Box, `*mut` *is* nullable. This means it 32 | can't benefit from the null pointer optimization. Instead, we'll be using `null` 33 | to represent None. 34 | 35 | So how do we get a null pointer? There's a few ways, but I prefer to use 36 | `std::ptr::null_mut()`. If you want, you can also use `0 as *mut _`, but that 37 | just seems so *messy*. 38 | 39 | ```rust 40 | use std::ptr; 41 | 42 | // defns... 43 | 44 | impl List { 45 | pub fn new() -> Self { 46 | List { head: None, tail: ptr::null_mut() } 47 | } 48 | } 49 | ``` 50 | 51 | ```text 52 | cargo build 53 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 54 | src/fifth.rs:4:5: 4:18 warning: struct field is never used: `head`, #[warn(dead_code)] on by default 55 | src/fifth.rs:4 head: Link, 56 | ^~~~~~~~~~~~~ 57 | src/fifth.rs:5:5: 5:23 warning: struct field is never used: `tail`, #[warn(dead_code)] on by default 58 | src/fifth.rs:5 tail: *mut Node, 59 | ^~~~~~~~~~~~~~~~~~ 60 | src/fifth.rs:11:5: 11:12 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 61 | src/fifth.rs:11 elem: T, 62 | ^~~~~~~ 63 | src/fifth.rs:12:5: 12:18 warning: struct field is never used: `next`, #[warn(dead_code)] on by default 64 | src/fifth.rs:12 next: Link, 65 | ^~~~~~~~~~~~~ 66 | ``` 67 | 68 | *shush* compiler, I will use them soon. 69 | 70 | Alright, let's move on to writing `push` again. This time, instead of grabbing 71 | an `Option<&mut Node>` after we insert, we're just going to grab a 72 | `*mut Node` to the insides of the Box right away. We know we can soundly do 73 | this because the contents of a Box has a stable address, even if we move the 74 | Box around. Of course, this isn't *safe*, because if we just drop the Box we'll 75 | have a pointer to freed memory. 76 | 77 | How do we make a raw pointer from a normal pointer? Coercions! If a variable 78 | is declared to be a raw pointer, a normal reference will coerce into it: 79 | 80 | ```rust 81 | let raw_tail: *mut _ = &mut *new_tail; 82 | ``` 83 | 84 | We have all the info we need. We can translate our code into, approximately, 85 | the previous reference version: 86 | 87 | ```rust 88 | pub fn push(&mut self, elem: T) { 89 | let mut new_tail = Box::new(Node { 90 | elem: elem, 91 | next: None, 92 | }); 93 | 94 | let raw_tail: *mut _ = &mut *new_tail; 95 | 96 | // .is_null checks for null, equivalent to checking for None 97 | if !self.tail.is_null() { 98 | // If the old tail existed, update it to point to the new tail 99 | self.tail.next = Some(new_tail); 100 | } else { 101 | // Otherwise, update the head to point to it 102 | self.head = Some(new_tail); 103 | } 104 | 105 | self.tail = raw_tail; 106 | } 107 | ``` 108 | 109 | ```text 110 | > cargo build 111 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 112 | src/fifth.rs:32:13: 32:27 error: attempted access of field `next` on type `*mut fifth::Node`, but no field with that name was found 113 | src/fifth.rs:32 self.tail.next = Some(new_tail); 114 | ^~~~~~~~~~~~~~ 115 | error: aborting due to previous error 116 | Could not compile `lists`. 117 | ``` 118 | 119 | Huh? We have a pointer to a Node, why can't we get the `next` field? 120 | 121 | Rust is kinda a jerk when you use raw pointers. To access the contents of a 122 | raw pointer, it insists that we manually deref them, because it's such an unsafe 123 | operation. So let's do that: 124 | 125 | ```rust 126 | *self.tail.next = Some(new_tail); 127 | ``` 128 | 129 | ```text 130 | > cargo build 131 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 132 | src/fifth.rs:31:14: 31:28 error: attempted access of field `next` on type `*mut fifth::Node`, but no field with that name was found 133 | src/fifth.rs:31 *self.tail.next = Some(new_tail); 134 | ^~~~~~~~~~~~~~ 135 | error: aborting due to previous error 136 | Could not compile `lists`. 137 | ``` 138 | 139 | Uuuugh operator precedence. 140 | 141 | ```rust 142 | (*self.tail).next = Some(new_tail); 143 | ``` 144 | 145 | ```text 146 | > cargo build 147 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 148 | src/fifth.rs:31:14: 31:24 error: dereference of raw pointer requires unsafe function or block [E0133] 149 | src/fifth.rs:31 (*self.tail).next = Some(new_tail); 150 | ^~~~~~~~~~ 151 | src/fifth.rs:31:14: 31:24 help: run `rustc --explain E0133` to see a detailed explanation 152 | error: aborting due to previous error 153 | Could not compile `lists`. 154 | ``` 155 | 156 | THIS SHOULDN'T BE THIS HARD. 157 | 158 | Remember how I said Unsafe Rust is like an FFI language for Safe Rust? Well, the 159 | compiler wants us to explicitly delimit where we're doing this FFI-ing. We have 160 | two options. First, we can mark our *entire* function as unsafe, in which case 161 | it becomes an Unsafe Rust function and can only be called in an `unsafe` 162 | context. This isn't great, because we want our list to be safe to use. Second, 163 | we can write an `unsafe` block inside our function, to delimit the FFI boundary. 164 | This declares the overall function to be safe. Let's do that one: 165 | 166 | 167 | ```rust 168 | pub fn push(&mut self, elem: T) { 169 | let mut new_tail = Box::new(Node { 170 | elem: elem, 171 | next: None, 172 | }); 173 | 174 | let raw_tail: *mut _ = &mut *new_tail; 175 | 176 | // Put the box in the right place, and then grab a reference to its Node 177 | if !self.tail.is_null() { 178 | // If the old tail existed, update it to point to the new tail 179 | unsafe { 180 | (*self.tail).next = Some(new_tail); 181 | } 182 | } else { 183 | // Otherwise, update the head to point to it 184 | self.head = Some(new_tail); 185 | } 186 | 187 | self.tail = raw_tail; 188 | } 189 | ``` 190 | 191 | ```text 192 | > cargo build 193 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 194 | src/fifth.rs:11:5: 11:12 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 195 | src/fifth.rs:11 elem: T, 196 | ^~~~~~~ 197 | ``` 198 | 199 | Yay! 200 | 201 | It's kind've interesting that that's the *only* place we've had to write an 202 | unsafe block so far. We do raw pointer stuff all over the place, what's up with 203 | that? 204 | 205 | It turns out that Rust is a massive rules-lawyer pedant when it comes to 206 | `unsafe`. We quite reasonably want to maximize the set of Safe Rust programs, 207 | because those are programs we can be much more confident in. To accomplish this, 208 | Rust carefully carves out a minimal surface area for unsafety. Note that all 209 | the other places we've worked with raw pointers has been *assigning* them, or 210 | just observing whether they're null or not. 211 | 212 | If you never actually dereference a raw pointer *those are totally safe things 213 | to do*. You're just reading and writing an integer! The only time you can 214 | actually get into trouble with a raw pointer is if you actually dereference it. 215 | So Rust says *only* that operation is unsafe, and everything else is totally 216 | safe. 217 | 218 | Super. Pedantic. But technically correct. 219 | 220 | However this raises an interesting problem: although we're supposed to delimit 221 | the scope of the unsafety with the `unsafe` block, it actually depends on 222 | state that was established outside of the block. Outside of the function, even! 223 | 224 | This is what I call unsafe *taint*. As soon as you use `unsafe` in a module, 225 | that whole module is tainted with unsafety. Everything has to be correctly 226 | written in order to make sure that invariants are upheld for the unsafe code. 227 | 228 | This taint is manageable because of *privacy*. Outside of our module, all of our 229 | struct fields are totally private, so no one else can mess with our state in 230 | arbitrary ways. As long as no combination of the APIs we expose causes bad stuff 231 | to happen, as far as an outside observer is concerned, all of our code is safe! 232 | And really, this is no different from the FFI case. No one needs care 233 | if some python math library shells out to C as long as it exposes a safe 234 | interface. 235 | 236 | Anyway, let's move on to `pop`, which is pretty much verbatim the reference 237 | version: 238 | 239 | ```rust 240 | pub fn pop(&mut self) -> Option { 241 | self.head.take().map(|head| { 242 | let head = *head; 243 | self.head = head.next; 244 | 245 | if self.head.is_none() { 246 | self.tail = ptr::null_mut(); 247 | } 248 | 249 | head.elem 250 | }) 251 | } 252 | ``` 253 | 254 | Again we see another case where safety is stateful. If we fail to null out the 255 | tail pointer in *this* function, we'll see no problems at all. However 256 | subsequent calls to `push` will start writing to the dangling tail! 257 | 258 | Let's test it out: 259 | 260 | ```rust 261 | #[cfg(test)] 262 | mod test { 263 | use super::List; 264 | #[test] 265 | fn basics() { 266 | let mut list = List::new(); 267 | 268 | // Check empty list behaves right 269 | assert_eq!(list.pop(), None); 270 | 271 | // Populate list 272 | list.push(1); 273 | list.push(2); 274 | list.push(3); 275 | 276 | // Check normal removal 277 | assert_eq!(list.pop(), Some(1)); 278 | assert_eq!(list.pop(), Some(2)); 279 | 280 | // Push some more just to make sure nothing's corrupted 281 | list.push(4); 282 | list.push(5); 283 | 284 | // Check normal removal 285 | assert_eq!(list.pop(), Some(3)); 286 | assert_eq!(list.pop(), Some(4)); 287 | 288 | // Check exhaustion 289 | assert_eq!(list.pop(), Some(5)); 290 | assert_eq!(list.pop(), None); 291 | 292 | // Check the exhaustion case fixed the pointer right 293 | list.push(6); 294 | list.push(7); 295 | 296 | // Check normal removal 297 | assert_eq!(list.pop(), Some(6)); 298 | assert_eq!(list.pop(), Some(7)); 299 | assert_eq!(list.pop(), None); 300 | } 301 | } 302 | ``` 303 | 304 | This is just the stack test, but with the expected `pop` results flipped around. 305 | I also added some extra steps and the end to make sure that tail-pointer 306 | corruption case in `pop` doesn't occur. 307 | 308 | ```text 309 | cargo test 310 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 311 | Running target/debug/lists-5c71138492ad4b4a 312 | 313 | running 8 tests 314 | test first::test::basics ... ok 315 | test second::test::basics ... ok 316 | test fifth::test::basics ... ok 317 | test second::test::iter ... ok 318 | test third::test::iter ... ok 319 | test second::test::iter_mut ... ok 320 | test second::test::into_iter ... ok 321 | test third::test::basics ... ok 322 | 323 | test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured 324 | 325 | Doc-tests lists 326 | 327 | running 0 tests 328 | 329 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 330 | ``` 331 | 332 | Gold Star! 333 | 334 | 335 | -------------------------------------------------------------------------------- /fifth-extras.md: -------------------------------------------------------------------------------- 1 | % Extra Junk 2 | 3 | Now that `push` and `pop` are written, everything else is exactly the same 4 | as the stack case. Only operations that change the length of the list need 5 | to actually worry about the tail pointer. 6 | 7 | So let's just steal all that from our second list (be sure to reverse the 8 | expected test output): 9 | 10 | ```rust 11 | // ... 12 | 13 | pub struct IntoIter(List); 14 | 15 | pub struct Iter<'a, T:'a> { 16 | next: Option<&'a Node>, 17 | } 18 | 19 | pub struct IterMut<'a, T: 'a> { 20 | next: Option<&'a mut Node>, 21 | } 22 | 23 | 24 | 25 | 26 | impl List { 27 | // ... 28 | 29 | pub fn peek(&self) -> Option<&T> { 30 | self.head.as_ref().map(|node| { 31 | &node.elem 32 | }) 33 | } 34 | 35 | pub fn peek_mut(&mut self) -> Option<&mut T> { 36 | self.head.as_mut().map(|node| { 37 | &mut node.elem 38 | }) 39 | } 40 | 41 | pub fn into_iter(self) -> IntoIter { 42 | IntoIter(self) 43 | } 44 | 45 | pub fn iter(&self) -> Iter { 46 | Iter { next: self.head.as_ref().map(|node| &**node) } 47 | } 48 | 49 | pub fn iter_mut(&mut self) -> IterMut { 50 | IterMut { next: self.head.as_mut().map(|node| &mut **node) } 51 | } 52 | } 53 | 54 | impl Drop for List { 55 | fn drop(&mut self) { 56 | let mut cur_link = self.head.take(); 57 | while let Some(mut boxed_node) = cur_link { 58 | cur_link = boxed_node.next.take(); 59 | } 60 | } 61 | } 62 | 63 | impl Iterator for IntoIter { 64 | type Item = T; 65 | fn next(&mut self) -> Option { 66 | self.0.pop() 67 | } 68 | } 69 | 70 | impl<'a, T> Iterator for Iter<'a, T> { 71 | type Item = &'a T; 72 | 73 | fn next(&mut self) -> Option { 74 | self.next.map(|node| { 75 | self.next = node.next.as_ref().map(|node| &**node); 76 | &node.elem 77 | }) 78 | } 79 | } 80 | 81 | impl<'a, T> Iterator for IterMut<'a, T> { 82 | type Item = &'a mut T; 83 | 84 | fn next(&mut self) -> Option { 85 | self.next.take().map(|node| { 86 | self.next = node.next.as_mut().map(|node| &mut **node); 87 | &mut node.elem 88 | }) 89 | } 90 | } 91 | 92 | 93 | 94 | 95 | 96 | #[cfg(test)] 97 | mod test { 98 | // ... 99 | 100 | #[test] 101 | fn into_iter() { 102 | let mut list = List::new(); 103 | list.push(1); list.push(2); list.push(3); 104 | 105 | let mut iter = list.into_iter(); 106 | assert_eq!(iter.next(), Some(1)); 107 | assert_eq!(iter.next(), Some(2)); 108 | assert_eq!(iter.next(), Some(3)); 109 | assert_eq!(iter.next(), None); 110 | } 111 | 112 | #[test] 113 | fn iter() { 114 | let mut list = List::new(); 115 | list.push(1); list.push(2); list.push(3); 116 | 117 | let mut iter = list.iter(); 118 | assert_eq!(iter.next(), Some(&1)); 119 | assert_eq!(iter.next(), Some(&2)); 120 | assert_eq!(iter.next(), Some(&3)); 121 | assert_eq!(iter.next(), None); 122 | } 123 | 124 | #[test] 125 | fn iter_mut() { 126 | let mut list = List::new(); 127 | list.push(1); list.push(2); list.push(3); 128 | 129 | let mut iter = list.iter_mut(); 130 | assert_eq!(iter.next(), Some(&mut 1)); 131 | assert_eq!(iter.next(), Some(&mut 2)); 132 | assert_eq!(iter.next(), Some(&mut 3)); 133 | assert_eq!(iter.next(), None); 134 | } 135 | } 136 | 137 | ``` 138 | 139 | ```text 140 | > cargo test 141 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 142 | Running target/debug/lists-5c71138492ad4b4a 143 | 144 | running 11 tests 145 | test fifth::test::basics ... ok 146 | test fifth::test::iter_mut ... ok 147 | test fifth::test::into_iter ... ok 148 | test first::test::basics ... ok 149 | test fifth::test::iter ... ok 150 | test second::test::iter ... ok 151 | test second::test::iter_mut ... ok 152 | test second::test::into_iter ... ok 153 | test second::test::basics ... ok 154 | test third::test::basics ... ok 155 | test third::test::iter ... ok 156 | 157 | test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured 158 | 159 | Doc-tests lists 160 | 161 | running 0 tests 162 | 163 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 164 | 165 | ``` 166 | 167 | 168 | Shout-outs to copy-paste programming. 169 | 170 | At first I thought we'd have to mess around with IntoIter, but we still 171 | conveniently pop in iteration order! 172 | -------------------------------------------------------------------------------- /fifth-final.md: -------------------------------------------------------------------------------- 1 | % Final Code 2 | 3 | Alright, so with a teeny-tiny dash of unsafety we managed to get a linear 4 | time improvement over the naive safe queue, and we managed to reuse almost 5 | all of the logic from the safe stack! 6 | 7 | We also notably *didn't* have to write any crazy Rc or RefCell stuff. 8 | 9 | ```rust 10 | #fn main() {} 11 | use std::ptr; 12 | 13 | pub struct List { 14 | head: Link, 15 | tail: *mut Node, 16 | } 17 | 18 | type Link = Option>>; 19 | 20 | struct Node { 21 | elem: T, 22 | next: Link, 23 | } 24 | 25 | pub struct IntoIter(List); 26 | 27 | pub struct Iter<'a, T:'a> { 28 | next: Option<&'a Node>, 29 | } 30 | 31 | pub struct IterMut<'a, T: 'a> { 32 | next: Option<&'a mut Node>, 33 | } 34 | 35 | 36 | 37 | 38 | 39 | 40 | impl List { 41 | pub fn new() -> Self { 42 | List { head: None, tail: ptr::null_mut() } 43 | } 44 | 45 | pub fn push(&mut self, elem: T) { 46 | let mut new_tail = Box::new(Node { 47 | elem: elem, 48 | next: None, 49 | }); 50 | 51 | let raw_tail: *mut _ = &mut *new_tail; 52 | 53 | if !self.tail.is_null() { 54 | unsafe { 55 | (*self.tail).next = Some(new_tail); 56 | } 57 | } else { 58 | self.head = Some(new_tail); 59 | } 60 | 61 | self.tail = raw_tail; 62 | } 63 | 64 | pub fn pop(&mut self) -> Option { 65 | self.head.take().map(|head| { 66 | let head = *head; 67 | self.head = head.next; 68 | 69 | if self.head.is_none() { 70 | self.tail = ptr::null_mut(); 71 | } 72 | 73 | head.elem 74 | }) 75 | } 76 | 77 | pub fn peek(&self) -> Option<&T> { 78 | self.head.as_ref().map(|node| { 79 | &node.elem 80 | }) 81 | } 82 | 83 | pub fn peek_mut(&mut self) -> Option<&mut T> { 84 | self.head.as_mut().map(|node| { 85 | &mut node.elem 86 | }) 87 | } 88 | 89 | pub fn into_iter(self) -> IntoIter { 90 | IntoIter(self) 91 | } 92 | 93 | pub fn iter(&self) -> Iter { 94 | Iter { next: self.head.as_ref().map(|node| &**node) } 95 | } 96 | 97 | pub fn iter_mut(&mut self) -> IterMut { 98 | IterMut { next: self.head.as_mut().map(|node| &mut **node) } 99 | } 100 | } 101 | 102 | impl Drop for List { 103 | fn drop(&mut self) { 104 | let mut cur_link = self.head.take(); 105 | while let Some(mut boxed_node) = cur_link { 106 | cur_link = boxed_node.next.take(); 107 | } 108 | } 109 | } 110 | 111 | impl Iterator for IntoIter { 112 | type Item = T; 113 | fn next(&mut self) -> Option { 114 | self.0.pop() 115 | } 116 | } 117 | 118 | impl<'a, T> Iterator for Iter<'a, T> { 119 | type Item = &'a T; 120 | 121 | fn next(&mut self) -> Option { 122 | self.next.map(|node| { 123 | self.next = node.next.as_ref().map(|node| &**node); 124 | &node.elem 125 | }) 126 | } 127 | } 128 | 129 | impl<'a, T> Iterator for IterMut<'a, T> { 130 | type Item = &'a mut T; 131 | 132 | fn next(&mut self) -> Option { 133 | self.next.take().map(|node| { 134 | self.next = node.next.as_mut().map(|node| &mut **node); 135 | &mut node.elem 136 | }) 137 | } 138 | } 139 | 140 | 141 | 142 | #[cfg(test)] 143 | mod test { 144 | use super::List; 145 | #[test] 146 | fn basics() { 147 | let mut list = List::new(); 148 | 149 | // Check empty list behaves right 150 | assert_eq!(list.pop(), None); 151 | 152 | // Populate list 153 | list.push(1); 154 | list.push(2); 155 | list.push(3); 156 | 157 | // Check normal removal 158 | assert_eq!(list.pop(), Some(1)); 159 | assert_eq!(list.pop(), Some(2)); 160 | 161 | // Push some more just to make sure nothing's corrupted 162 | list.push(4); 163 | list.push(5); 164 | 165 | // Check normal removal 166 | assert_eq!(list.pop(), Some(3)); 167 | assert_eq!(list.pop(), Some(4)); 168 | 169 | // Check exhaustion 170 | assert_eq!(list.pop(), Some(5)); 171 | assert_eq!(list.pop(), None); 172 | 173 | // Check the exhaustion case fixed the pointer right 174 | list.push(6); 175 | list.push(7); 176 | 177 | // Check normal removal 178 | assert_eq!(list.pop(), Some(6)); 179 | assert_eq!(list.pop(), Some(7)); 180 | assert_eq!(list.pop(), None); 181 | } 182 | 183 | #[test] 184 | fn into_iter() { 185 | let mut list = List::new(); 186 | list.push(1); list.push(2); list.push(3); 187 | 188 | let mut iter = list.into_iter(); 189 | assert_eq!(iter.next(), Some(1)); 190 | assert_eq!(iter.next(), Some(2)); 191 | assert_eq!(iter.next(), Some(3)); 192 | assert_eq!(iter.next(), None); 193 | } 194 | 195 | #[test] 196 | fn iter() { 197 | let mut list = List::new(); 198 | list.push(1); list.push(2); list.push(3); 199 | 200 | let mut iter = list.iter(); 201 | assert_eq!(iter.next(), Some(&1)); 202 | assert_eq!(iter.next(), Some(&2)); 203 | assert_eq!(iter.next(), Some(&3)); 204 | assert_eq!(iter.next(), None); 205 | } 206 | 207 | #[test] 208 | fn iter_mut() { 209 | let mut list = List::new(); 210 | list.push(1); list.push(2); list.push(3); 211 | 212 | let mut iter = list.iter_mut(); 213 | assert_eq!(iter.next(), Some(&mut 1)); 214 | assert_eq!(iter.next(), Some(&mut 2)); 215 | assert_eq!(iter.next(), Some(&mut 3)); 216 | assert_eq!(iter.next(), None); 217 | } 218 | } 219 | ``` 220 | -------------------------------------------------------------------------------- /fifth-layout.md: -------------------------------------------------------------------------------- 1 | % Layout 2 | 3 | So what's a singly-linked queue like? Well, when we had a singly linked stack 4 | we pushed onto one end of the list, and then popped off the same end. The only 5 | difference between a stack and a queue is that a queue pops off the *other* 6 | end. So from our stack implementation we have: 7 | 8 | ```text 9 | input list: 10 | [Some(ptr)] -> (A, Some(ptr)) -> (B, None) 11 | 12 | stack push X: 13 | [Some(ptr)] -> (X, Some(ptr)) -> (A, Some(ptr)) -> (B, None) 14 | 15 | stack pop: 16 | [Some(ptr)] -> (A, Some(ptr)) -> (B, None) 17 | ``` 18 | 19 | To make a queue, we just need to decide which operation to move to the 20 | end of the list: push, or pop? Since our list is singly-linked, we can 21 | actually move *either* operation to the end with the same amount of effort. 22 | 23 | To move `push` to the end, we just walk all the way to the `None` and set it 24 | to Some with the new element. 25 | 26 | ```text 27 | input list: 28 | [Some(ptr)] -> (A, Some(ptr)) -> (B, None) 29 | 30 | flipped push X: 31 | [Some(ptr)] -> (A, Some(ptr)) -> (B, Some(ptr)) -> (X, None) 32 | ``` 33 | 34 | To move `pop` to the end, we just walk all the way to the node *before* the 35 | None, and `take` it: 36 | 37 | ```text 38 | input list: 39 | [Some(ptr)] -> (A, Some(ptr)) -> (B, Some(ptr)) -> (X, None) 40 | 41 | flipped pop: 42 | [Some(ptr)] -> (A, Some(ptr)) -> (B, None) 43 | ``` 44 | 45 | We could do this today and call it quits, but that would stink! Both of these 46 | operations walk over the *entire* list. Some would argue that such a queue 47 | implementation is indeed a queue because it exposes the right interface. However 48 | I believe that performance guarantees are part of the interface. I don't care 49 | about precise assymptotic bounds, but rather "fast" and "slow". Queues guarantee 50 | that push and pop are fast, and walking over the whole list is definitely *not* 51 | fast. 52 | 53 | One key observation is that we're wasting a ton of work doing *the same thing* 54 | over and over. Can we memoize this work? Why, yes! We can store a pointer to 55 | the end of the list, and just jump straight to there! 56 | 57 | It turns out that only one inversion of `push` and `pop` works with this. 58 | Because our list is singly-linked, we can't effeciently walk *backwards* in 59 | the list. To invert `pop` we would have to move the "tail" pointer backwards. 60 | But if we instead invert `push` we only have to move the "head" pointer 61 | forwards, which is easy. 62 | 63 | Let's try that: 64 | 65 | ```rust 66 | use std::mem; 67 | # fn main() {} 68 | 69 | pub struct List { 70 | head: Link, 71 | tail: Link, // NEW! 72 | } 73 | 74 | type Link = Option>>; 75 | 76 | struct Node { 77 | elem: T, 78 | next: Link, 79 | } 80 | 81 | impl List { 82 | pub fn new() -> Self { 83 | List { head: None, tail: None } 84 | } 85 | 86 | pub fn push(&mut self, elem: T) { 87 | let new_tail = Box::new(Node { 88 | elem: elem, 89 | // When you push onto the tail, your next is always None 90 | next: None, 91 | }); 92 | 93 | // swap the old tail to point to the new tail 94 | let old_tail = mem::replace(&mut self.tail, Some(new_tail)); 95 | 96 | match old_tail { 97 | Some(mut old_tail) => { 98 | // If the old tail existed, update it to point to the new tail 99 | old_tail.next = Some(new_tail); 100 | } 101 | None => { 102 | // Otherwise, update the head to point to it 103 | self.head = Some(new_tail); 104 | } 105 | } 106 | } 107 | } 108 | ``` 109 | 110 | I'm going a bit faster with the impl details now since we should be pretty 111 | comfortable with this sort of thing. Not that you should necessarily expect 112 | to produce this code on the first try. I'm just skipping over some of the 113 | trail-and-error we've had to deal with before. I actually made a ton of mistakes 114 | writing this code that I'm not showing. You can only see me leave off a `mut` or 115 | `;` so many times before it stops being instructive. Don't worry, we'll see 116 | plenty of *other* error messages! 117 | 118 | ```text 119 | > cargo build 120 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 121 | src/fifth.rs:33:38: 33:46 error: use of moved value: `new_tail` [E0382] 122 | src/fifth.rs:33 old_tail.next = Some(new_tail); 123 | ^~~~~~~~ 124 | src/fifth.rs:28:58: 28:66 note: `new_tail` moved here because it has type `Box>`, which is non-copyable 125 | src/fifth.rs:28 let old_tail = mem::replace(&mut self.tail, Some(new_tail)); 126 | ^~~~~~~~ 127 | src/fifth.rs:37:34: 37:42 error: use of moved value: `new_tail` [E0382] 128 | src/fifth.rs:37 self.head = Some(new_tail); 129 | ^~~~~~~~ 130 | src/fifth.rs:28:58: 28:66 note: `new_tail` moved here because it has type `Box>`, which is non-copyable 131 | src/fifth.rs:28 let old_tail = mem::replace(&mut self.tail, Some(new_tail)); 132 | ^~~~~~~~ 133 | error: aborting due to 2 previous errors 134 | Could not compile `lists`. 135 | ``` 136 | 137 | Shoot! 138 | 139 | > use of moved value: `new_tail` 140 | 141 | Box doesn't implement Copy, so we can't just assign it to two locations. More 142 | importantly, Box *owns* the thing it points to, and will try to free it when 143 | it's dropped. If our `push` implementation compiled, we'd double-free the tail 144 | of our list! Actually, as written, our code would free the old_tail on every 145 | push. Yikes! 🙀 146 | 147 | Alright, well we know how to make a non-owning pointer. That's just a reference! 148 | 149 | ```rust 150 | # fn main() {} 151 | pub struct List { 152 | head: Link, 153 | tail: Option<&mut Node>, // NEW! 154 | } 155 | 156 | type Link = Option>>; 157 | 158 | struct Node { 159 | elem: T, 160 | next: Link, 161 | } 162 | 163 | impl List { 164 | pub fn new() -> Self { 165 | List { head: None, tail: None } 166 | } 167 | 168 | pub fn push(&mut self, elem: T) { 169 | let new_tail = Box::new(Node { 170 | elem: elem, 171 | // When you push onto the tail, your next is always None 172 | next: None, 173 | }); 174 | 175 | // Put the box in the right place, and then grab a reference to its Node 176 | let new_tail = match self.tail.take() { 177 | Some(old_tail) => { 178 | // If the old tail existed, update it to point to the new tail 179 | old_tail.next = Some(new_tail); 180 | old_tail.next.as_mut().map(|node| &mut **node) 181 | } 182 | None => { 183 | // Otherwise, update the head to point to it 184 | self.head = Some(new_tail); 185 | self.head.as_mut().map(|node| &mut **node) 186 | } 187 | }; 188 | 189 | self.tail = new_tail; 190 | } 191 | } 192 | ``` 193 | 194 | Nothing too tricky here. Same basic idea as the previous code, except we're 195 | using some of that implicit return goodness to extract the tail reference from 196 | wherever we stuff the actual Box. 197 | 198 | ```text 199 | > cargo build 200 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 201 | src/fifth.rs:3:18: 3:30 error: missing lifetime specifier [E0106] 202 | src/fifth.rs:3 tail: Option<&mut Node>, // NEW! 203 | ^~~~~~~~~~~~ 204 | src/fifth.rs:3:18: 3:30 help: run `rustc --explain E0106` to see a detailed explanation 205 | error: aborting due to previous error 206 | Could not compile `lists`. 207 | ``` 208 | 209 | Oh right, we need to give references in types lifetimes. Hmm... what's the 210 | lifetime of this reference? Well, this seems like IterMut, right? Let's try 211 | what we did for IterMut, and just add a generic `'a`: 212 | 213 | ```rust 214 | # fn main() {} 215 | pub struct List<'a, T: 'a> { 216 | head: Link, 217 | tail: Option<&'a mut Node>, // NEW! 218 | } 219 | 220 | type Link = Option>>; 221 | 222 | struct Node { 223 | elem: T, 224 | next: Link, 225 | } 226 | 227 | impl<'a, T> List<'a, T> { 228 | pub fn new() -> Self { 229 | List { head: None, tail: None } 230 | } 231 | 232 | pub fn push(&mut self, elem: T) { 233 | let new_tail = Box::new(Node { 234 | elem: elem, 235 | // When you push onto the tail, your next is always None 236 | next: None, 237 | }); 238 | 239 | // Put the box in the right place, and then grab a reference to its Node 240 | let new_tail = match self.tail.take() { 241 | Some(old_tail) => { 242 | // If the old tail existed, update it to point to the new tail 243 | old_tail.next = Some(new_tail); 244 | old_tail.next.as_mut().map(|node| &mut **node) 245 | } 246 | None => { 247 | // Otherwise, update the head to point to it 248 | self.head = Some(new_tail); 249 | self.head.as_mut().map(|node| &mut **node) 250 | } 251 | }; 252 | 253 | self.tail = new_tail; 254 | } 255 | } 256 | ``` 257 | 258 | ```text 259 | cargo build 260 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 261 | src/fifth.rs:35:27: 35:35 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements 262 | src/fifth.rs:35 self.head.as_mut().map(|node| &mut **node) 263 | ^~~~~~~~ 264 | src/fifth.rs:18:5: 40:6 help: consider using an explicit lifetime parameter as shown: fn push(&'a mut self, elem: T) 265 | src/fifth.rs:18 pub fn push(&mut self, elem: T) { 266 | src/fifth.rs:19 let new_tail = Box::new(Node { 267 | src/fifth.rs:20 elem: elem, 268 | src/fifth.rs:21 // When you push onto the tail, your next is always None 269 | src/fifth.rs:22 next: None, 270 | src/fifth.rs:23 }); 271 | ... 272 | error: aborting due to previous error 273 | ``` 274 | 275 | Oh lord. When the compiler starts telling us to just start adding lifetimes in 276 | random places, it's a red flag that the compiler is deeply confused. But uh... 277 | ok let's try that I guess? 278 | 279 | ```rust,ignore 280 | pub fn push(&'a mut self, elem: T) { 281 | ``` 282 | 283 | ```text 284 | cargo build 285 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 286 | src/fifth.rs:9:5: 9:12 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 287 | src/fifth.rs:9 elem: T, 288 | ^~~~~~~ 289 | ``` 290 | 291 | Oh, hey, that worked! Great! 292 | 293 | Let's just do `pop` too: 294 | 295 | ```rust 296 | pub fn pop(&'a mut self) -> Option { 297 | // Grab the list's current head 298 | self.head.take().map(|head| { 299 | let head = *head; 300 | self.head = head.next; 301 | 302 | // If we're out of `head`, make sure to set the tail to `None`. 303 | if self.head.is_none() { 304 | self.tail = None; 305 | } 306 | 307 | head.elem 308 | }) 309 | } 310 | ``` 311 | 312 | Let's try to just write a quick test for that: 313 | 314 | ``` 315 | mod test { 316 | use super::List; 317 | #[test] 318 | fn basics() { 319 | let mut list = List::new(); 320 | 321 | // Check empty list behaves right 322 | assert_eq!(list.pop(), None); 323 | 324 | // Populate list 325 | list.push(1); 326 | list.push(2); 327 | list.push(3); 328 | 329 | // Check normal removal 330 | assert_eq!(list.pop(), Some(1)); 331 | assert_eq!(list.pop(), Some(2)); 332 | 333 | // Push some more just to make sure nothing's corrupted 334 | list.push(4); 335 | list.push(5); 336 | 337 | // Check normal removal 338 | assert_eq!(list.pop(), Some(3)); 339 | assert_eq!(list.pop(), Some(4)); 340 | 341 | // Check exhaustion 342 | assert_eq!(list.pop(), Some(5)); 343 | assert_eq!(list.pop(), None); 344 | } 345 | } 346 | ``` 347 | 348 | ```text 349 | cargo test 350 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 351 | src/fifth.rs:68:9: 68:13 error: cannot borrow `list` as mutable more than once at a time 352 | src/fifth.rs:68 list.push(2); 353 | ^~~~ 354 | src/fifth.rs:66:9: 66:13 note: previous borrow of `list` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `list` until the borrow ends 355 | src/fifth.rs:66 list.push(1); 356 | ^~~~ 357 | src/fifth.rs:70:6: 70:6 note: previous borrow ends here 358 | src/fifth.rs:59 fn basics() { 359 | ... 360 | src/fifth.rs:70 } 361 | ^ 362 | 363 | 364 | **NOT SHOWN: LITERALLY A THOUSAND LINES OF BORROW CHECK ERRORS** 365 | 366 | 367 | src/fifth.rs:84:20: 84:24 error: cannot borrow `list` as mutable more than once at a time 368 | src/fifth.rs:84 assert_eq!(list.pop(), None); 369 | ^~~~ 370 | :1:1: 9:39 note: in expansion of assert_eq! 371 | src/fifth.rs:84:9: 84:38 note: expansion site 372 | src/fifth.rs:83:20: 83:24 note: previous borrow of `list` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `list` until the borrow ends 373 | src/fifth.rs:83 assert_eq!(list.pop(), Some(1)); 374 | ^~~~ 375 | :1:1: 9:39 note: in expansion of assert_eq! 376 | src/fifth.rs:83:9: 83:41 note: expansion site 377 | src/fifth.rs:85:6: 85:6 note: previous borrow ends here 378 | src/fifth.rs:59 fn basics() { 379 | ... 380 | src/fifth.rs:85 } 381 | ^ 382 | error: aborting due to 66 previous errors 383 | ``` 384 | 385 | 🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀 386 | 387 | OH MY GEEZ WHAT. 388 | 389 | 66 borrow check errors. 390 | 391 | Oh my goodness. 392 | 393 | [I'm pretty sure we just hit this bug in the compiler](https://github.com/rust-lang/rust/issues/27485). 394 | 395 | But the compiler's not wrong for vomiting all over us. We just committed a 396 | cardinal Rust sin: we stored a reference to ourselves *inside ourselves*. 397 | Somehow, we managed to convince Rust that this totally made sense in our 398 | `push` and `pop` implementations (I was legitimately shocked we did). I believe 399 | the reason is that Rust can't yet tell that the reference is into ourselves 400 | from just `push` and `pop` -- or rather, Rust doesn't really have that notion 401 | at all. Reference-into-yourself falls over as an emergent behaviour. 402 | 403 | However as soon as we tried to *use* our list, everything quickly fell apart. 404 | When we call `push` or `pop`, we promptly store a reference to ourselves in 405 | ourselves and become *trapped*. We are literally borrowing ourselves. 406 | 407 | Our `pop` implementation hints at why this could be really dangerous: 408 | 409 | ```rust,ignore 410 | // ... 411 | if self.head.is_none() { 412 | self.tail = None; 413 | } 414 | ``` 415 | 416 | What if we forgot to do this? Then our tail would point to some node *that 417 | had been removed from the list*. Such a node would be instantly freed, and we'd 418 | have a dangling pointer which Rust was supposed to protect us from! 419 | 420 | And indeed Rust is protecting us from that kind of danger. Just in a very... 421 | **roundabout** way. 422 | 423 | So what can we do? Go back to `Rc>` hell? 424 | 425 | Please. No. 426 | 427 | No instead we're going to go off the rails and use *raw pointers*. 428 | Our layout is going to look like this: 429 | 430 | ```rust 431 | # fn main() {} 432 | pub struct List { 433 | head: Link, 434 | tail: *mut Node, // DANGER DANGER 435 | } 436 | 437 | type Link = Option>>; 438 | 439 | struct Node { 440 | elem: T, 441 | next: Link, 442 | } 443 | ``` 444 | 445 | And that's that. None of this wimpy reference-counted-dynamic-borrow-checking 446 | nonsense! Real. Hard. Unchecked. Pointers. 447 | 448 | Let's be C everyone. Let's be C all day. 449 | 450 | I'm home. I'm ready. 451 | 452 | Hello `unsafe`. 453 | 454 | -------------------------------------------------------------------------------- /fifth-unsafe.md: -------------------------------------------------------------------------------- 1 | % Unsafe Rust 2 | 3 | This is a serious, big, complicated, and dangerous topic. 4 | It's so serious that I wrote [an entire other book][nom] on it. 5 | 6 | The long and the short of it is that *every* language is actually unsafe as soon 7 | as you allow calling into other languages, because you can just have C do 8 | arbitrarily bad things. Yes: Java, Python, Ruby, Haskell... everyone is wildly 9 | unsafe in the face of Foreign Function Interfaces (FFI). 10 | 11 | Rust embraces this truth by splitting itself into two languages: Safe Rust, and 12 | Unsafe Rust. So far we've only worked with Safe Rust. It's completely 100% 13 | safe... except that it can FFI into Unsafe Rust. 14 | 15 | Unsafe Rust is a *superset* of Safe Rust. It's completely the same as Safe Rust in all its 16 | semantics and rules, you're just allowed to do a few *extra* things that are 17 | wildly unsafe and can cause the dreaded Undefined Behaviour that haunts C. 18 | 19 | Again, this is a really huge topic that has a lot of interesting corner cases. 20 | I *really* don't want to go really deep into it (well, I do. I did. [Read that 21 | book][nom]). That's ok, because with linked lists we can actually ignore almost 22 | all of it. 23 | 24 | The main Unsafe tool we'll be using are *raw pointers*. Raw pointers are 25 | basically C's pointers. They have no inherent aliasing rules. They have 26 | no lifetimes. The can be null. They can be dangling. They can point to 27 | uninitialized memory. The can be cast to and from integers. They can be 28 | cast to point to a different type. Mutability? Cast it. Pretty much everything 29 | goes, and that means pretty much anything can go wrong. 30 | 31 | This is some bad stuff and honestly you'll live a happier life never having 32 | to touch these. Unforunately, we want to write linked lists, and linked lists 33 | are awful. That means we're going to have to use unsafe pointers. 34 | 35 | There are two kinds of raw pointer: `*const T` and `*mut T`. These are meant to 36 | be `const T*` and `T*` from C, but we really don't care about what C thinks they 37 | mean that much. You can only dereference a `*const T` to an `&T`, but much like 38 | the mutability of a variable, this is just a lint against incorrect usage. At 39 | most it just means you have to cast the `*const` to a `*mut` first. Although if 40 | you don't actually have permission to mutate the referrent of the pointer, 41 | you're gonna have a bad time. 42 | 43 | Anyway, we'll get a better feel for this as we write some code. For now, 44 | `*mut T == &unchecked mut T`! 45 | 46 | [nom]: https://doc.rust-lang.org/nightly/nomicon/ 47 | -------------------------------------------------------------------------------- /fifth.md: -------------------------------------------------------------------------------- 1 | % An Unsafe Singly-Linked Queue 2 | 3 | Ok that reference-counted interior mutability stuff got a little out of 4 | control. Surely Rust doesn't really expect you to do that sort of thing 5 | in general? Well, yes and no. Rc and Refcell can be great for handling 6 | simple cases, but they can get unwieldy. Especially if you 7 | want to hide that it's happening. There's gotta be a better way! 8 | 9 | In this chapter we're going to roll back to singly linked lists and 10 | implement a singly-linked queue to dip our toes into *raw pointers* 11 | and *Unsafe Rust*. 12 | 13 | Let's add a new file called `fifth.rs`: 14 | 15 | ```rust 16 | // in lib.rs 17 | 18 | pub mod first; 19 | pub mod second; 20 | pub mod third; 21 | pub mod fourth; 22 | pub mod fifth; 23 | ``` 24 | 25 | Our code is largely going to be derived from second.rs, since a queue is 26 | mostly an augmentation of a stack in the world of linked lists. Still, we're 27 | going to go from scratch because there's some fundamental issues we want to 28 | address with layout and what-not. 29 | -------------------------------------------------------------------------------- /first-drop.md: -------------------------------------------------------------------------------- 1 | # Drop 2 | 3 | 我们现在可以创建一个栈,推入元素,弹出元素,甚至确认了一切都可以正常的工作! 4 | 5 | 我们需要担心列表元素的清理么?严格的说,根本不用!就像C++,Rust使用析构器来自动的处理使用完毕的资源。如果一个类型实现了叫做 Drop 的*特性(Trait)*,它就拥有一个析构器。特性是Rust对接口的特别术语。Drop特性有如下的接口: 6 | 7 | ``` 8 | pub trait Drop { 9 | fn drop(&mut self); 10 | } 11 | ``` 12 | 13 | 基本上是这个意思:“当对象退出作用域的时候,我会给你清理事务的第二次机会”。 14 | 15 | 如果你的类型里存放有实现了Drop的其他类型,而你想要调用它们的析构器,是不需要实际实现Drop的。对于List来说,我们想做的不过是把列表头丢弃,之后或许会接着丢弃一个`Box`。所有这些都会自动在一瞬间处理完成。 16 | 17 | 自动处理会很糟糕。 18 | 19 | 让我们考虑这个简单的列表。 20 | 21 | ```text 22 | list -> A -> B -> C 23 | ``` 24 | 25 | 当列表被丢弃时,它会先丢弃A,然后尝试丢弃B,然后会尝试丢弃C。现在你可能已经紧张起来了。这是递归代码,而递归代码会把栈爆掉! 26 | 27 | ```rust 28 | impl Drop for List { 29 | fn drop(&mut self) { 30 | // 注意:在实际Rust代码中你不能显式调用`drop`, 31 | // 我们假装自己是编译器! 32 | list.head.drop(); // 尾递归——好! 33 | } 34 | } 35 | 36 | impl Drop for Link { 37 | fn drop(&mut self) { 38 | match list.head { 39 | Link::Empty => {} // 完成! 40 | Link::More(ref mut boxed_node) => { 41 | boxed_node.drop(); // 尾递归——好! 42 | } 43 | } 44 | } 45 | } 46 | 47 | impl Drop for Box { 48 | fn drop(&mut self) { 49 | self.ptr.drop(); // 糟糕,不是尾递归! 50 | deallocate(self.ptr); 51 | } 52 | } 53 | 54 | impl Drop for Node { 55 | fn drop(&mut self) { 56 | self.next.drop(); 57 | } 58 | } 59 | ``` 60 | 61 | 我们不能在释放内存之后再丢弃Box的内容,所以没有办法以尾递归的形式进行drop!作为替代,我们必须为`List`手动编写一个迭代drop,来把节点从box中拿出来。 62 | 63 | ```rust 64 | impl Drop for List { 65 | fn drop(&mut self) { 66 | let mut cur_link = mem::replace(&mut self.head, Link::Empty); 67 | // `while let` == “在这个模式不匹配之前持续循环” 68 | while let Link::More(mut boxed_node) = cur_link { 69 | cur_link = mem::replace(&mut boxed_node.next, Link::Empty); 70 | // boxed_node在这里退出作用域然后被丢弃; 71 | // 但是其节点的`next`字段被设置为 Link::Empty 72 | // 所以没有多层递归产生。 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | ```text 79 | > cargo test 80 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 81 | Running target/debug/lists-5c71138492ad4b4a 82 | 83 | running 1 test 84 | test first::test::basics ... ok 85 | 86 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured 87 | 88 | Doc-tests lists 89 | 90 | running 0 tests 91 | 92 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 93 | ``` 94 | 95 | 棒极了! 96 | -------------------------------------------------------------------------------- /first-final.md: -------------------------------------------------------------------------------- 1 | # 最终代码 2 | 3 | 好吧,在6000词之后,这是我们实际写成的所有代码: 4 | 5 | ```rust 6 | use std::mem; 7 | 8 | pub struct List { 9 | head: Link, 10 | } 11 | 12 | enum Link { 13 | Empty, 14 | More(Box), 15 | } 16 | 17 | struct Node { 18 | elem: i32, 19 | next: Link, 20 | } 21 | 22 | impl List { 23 | pub fn new() -> Self { 24 | List { head: Link::Empty } 25 | } 26 | 27 | pub fn push(&mut self, elem: i32) { 28 | let new_node = Box::new(Node { 29 | elem: elem, 30 | next: mem::replace(&mut self.head, Link::Empty), 31 | }); 32 | 33 | self.head = Link::More(new_node); 34 | } 35 | 36 | pub fn pop(&mut self) -> Option { 37 | match mem::replace(&mut self.head, Link::Empty) { 38 | Link::Empty => None, 39 | Link::More(node) => { 40 | let node = *node; 41 | self.head = node.next; 42 | Some(node.elem) 43 | } 44 | } 45 | } 46 | } 47 | 48 | impl Drop for List { 49 | fn drop(&mut self) { 50 | let mut cur_link = mem::replace(&mut self.head, Link::Empty); 51 | while let Link::More(mut boxed_node) = cur_link { 52 | cur_link = mem::replace(&mut boxed_node.next, Link::Empty); 53 | } 54 | } 55 | } 56 | 57 | #[cfg(test)] 58 | mod test { 59 | use super::List; 60 | 61 | #[test] 62 | fn basics() { 63 | let mut list = List::new(); 64 | 65 | // Check empty list behaves right 66 | assert_eq!(list.pop(), None); 67 | 68 | // Populate list 69 | list.push(1); 70 | list.push(2); 71 | list.push(3); 72 | 73 | // Check normal removal 74 | assert_eq!(list.pop(), Some(3)); 75 | assert_eq!(list.pop(), Some(2)); 76 | 77 | // Push some more just to make sure nothing's corrupted 78 | list.push(4); 79 | list.push(5); 80 | 81 | // Check normal removal 82 | assert_eq!(list.pop(), Some(5)); 83 | assert_eq!(list.pop(), Some(4)); 84 | 85 | // Check exhaustion 86 | assert_eq!(list.pop(), Some(1)); 87 | assert_eq!(list.pop(), None); 88 | } 89 | } 90 | ``` 91 | 92 | 天。80行,而且其中的一半都是测试!嘛,我确实说过这第一个教程会花一些时间的! -------------------------------------------------------------------------------- /first-layout.md: -------------------------------------------------------------------------------- 1 | # 基本布局 2 | 3 | 好吧,所以链表是什么东西呢?大致上说,它是一大片相互指向的数据,以顺序连接起来(嘘,Linux内核!)。链表是过程式程序员应该用一切代价避免的东西,而函数程序员则在所有情况下使用它。那么,看起来向函数式程序员询问链表的定义是很公平的。他们可能会给你类似这样的定义: 4 | 5 | ```haskell 6 | List a = Empty | Elem a (List a) 7 | ``` 8 | 9 | 它可以大致读成“一个列表要么是空的,要么是一个元素接着一个列表”。这是用一个复合类型(sum type)表达的递归定义,而复合类型只是“一个可以拥有多种类型的值的类型”的酷炫叫法。Rust把复合类型称作`enum`!如果你是从一个C系语言过来的,那么这正是你所熟知并热爱的枚举类型,但是强大了许多。让我们把这个函数式的链表定义转录到Rust吧! 10 | 11 | 现在为了保持说明简单,我们会避开泛型。我们暂时只支持有符号的32位整数: 12 | 13 | ```rust,ignore 14 | // 在 first.rs 中 15 | 16 | // pub 表明我们想让这个模块外之外的人使用这个 List 17 | pub enum List { 18 | Empty, 19 | Elem(i32, List), 20 | } 21 | ``` 22 | 23 | 呼,这一点也不麻烦嘛。我们继续下去编译它吧: 24 | 25 | ```text 26 | > cargo build 27 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 28 | src/first.rs:1:1: 4:2 error: illegal recursive enum type; wrap the inner value in a box to make it representable [E0072] 29 | src/first.rs:1 pub enum List { 30 | src/first.rs:2 Empty, 31 | src/first.rs:3 Elem(T, List), 32 | src/first.rs:4 } 33 | error: aborting due to previous error 34 | Could not compile `lists`. 35 | ``` 36 | 37 | 不!!!!函数式程序员欺骗了我们!它让我们做了*不合法*的东西!这是圈套! 38 | 39 | …… 40 | 41 | 我冷静下来了。你冷静了么?如果我们真正去检查错误消息(而不是像我们中的某些人一样,准备逃出这个国家),我们就会发现rustc实际上在告诉我们如何解决这个问题: 42 | 43 | > illegal recursive enum type; wrap the inner value in a box to make it representable 44 | > 不合法的递归枚举类型;把值包装在一个box里让它变得可表示 45 | 好吧,`box`。那是什么东西?让我们 google `rust box`…… 46 | 47 | > [std::boxed::Box - Rust](https://doc.rust-lang.org/std/boxed/struct.Box.html) 48 | 49 | 看看接下来是啥…… 50 | 51 | > `pub struct Box(_);` 52 | > 53 | > A pointer type for heap allocation. 54 | > See the [module-level documentation](https://doc.rust-lang.org/std/boxed/) for more. 55 | 56 | *点击链接* 57 | 58 | > `Box`,或被随意的称为`box`,提供了Rust中最简单的堆内存分配的形式。Box提供了当次内存分配的所有权,并在退出作用域时销毁存放的内容。 59 | > 60 | > 示例 61 | > 62 | > 创建一个box 63 | > 64 | > `let x = Box::new(5);` 65 | > 66 | > 创建一个递归数据结构: 67 | > 68 | ``` 69 | #[derive(Debug)] 70 | enum List { 71 | Cons(T, Box>), 72 | Nil, 73 | } 74 | ``` 75 | > 76 | ``` 77 | fn main() { 78 | let list: List = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil)))); 79 | println!("{:?}", list); 80 | } 81 | ``` 82 | > 83 | > 这会打印 `Cons(1, Box(Cons(2, Box(Nil))))`. 84 | > 85 | > 递归的结构必须使用box包装,因为如果Cons的定义如下这样: 86 | > 87 | > `Cons(T, List),` 88 | > 89 | > 是不会工作的。这是因为List的大小由其中的元素数量所决定,所以我们无法决定为一个Cons分配多少内存。通过引入一个具有固定大小的Box,我们才知道Cons需要占用多少内存。 90 | 91 | 哇哦。这或许是我见过的最相关最有帮助的文档了。在里面的第一个东西就是*我们正在尝试写的东西,为什么它不能工作,以及如何修复它*。好耶,文档。 92 | 93 | OK,我们来完成它: 94 | 95 | ```rust 96 | pub enum List { 97 | Empty, 98 | Elem(i32, Box), 99 | } 100 | ``` 101 | 102 | ```text 103 | > cargo build 104 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 105 | ``` 106 | 107 | 嘿,它成功编译了! 108 | 109 | ……但这实际上是一个非常蠢的List的定义,出于以下的一些原因。 110 | 111 | 考虑一个拥有两个元素的列表: 112 | 113 | ```text 114 | [] = 栈 115 | () = 堆 116 | 117 | [Elem A, ptr] -> (Elem B, ptr) -> (Empty *垃圾*) 118 | ``` 119 | 120 | 这里有两个关键问题: 121 | 122 | * 我们创建了一个“实际上不是个节点”的节点 123 | * 其中的一个节点根本没分配在堆里 124 | 125 | 在表面上,这两个问题似乎相互抵消。我们分配了一个多余的节点,但有一个节点完全无需在堆里分配。然而,考虑我们链表的一个潜在的内存布局: 126 | 127 | ```text 128 | [ptr] -> (元素A, ptr) -> (元素B, *null*) 129 | ``` 130 | 131 | 在这个布局里,我们在堆里分配所有的元素。和第一个布局相比,核心的区别是多余的*垃圾*的消失。这个垃圾到底是什么?为了理解它,我们需要看一看enum是如何在内存中布局的。 132 | 133 | 通常的,如果我们有像这样的一个enum: 134 | 135 | ```rust 136 | enum Foo { 137 | D1(T1), 138 | D2(T2), 139 | ... 140 | Dn(Tn), 141 | } 142 | ``` 143 | 144 | 一个Foo需要保存一个整数,来指出它实际表示的是哪一个*变体*(`D1`, `D2`, .. `Dn`)。这是enum的*标签*(tag)。它也需要足够大的空间,来存储`T1`, `T2`, .. `Tn`中的最大元素(以及用来满足内存对齐要求的附加空间)。 145 | 146 | 一个很大的缺陷是,尽管`Empty`只存储了一位的信息,它却消耗了一个指针和一个元素的内存空间,因为它要随时准备成为一个`Elem`。因此第一种布局在堆里分配了一个充满垃圾的多余元素,比第二种布局消耗更多的空间。 147 | 148 | 让我们的一个元素不在堆中分配,或许也比所有元素都在堆中分配更糟。这是因为它给了我们一个*不一致的*节点内存布局。在推入和弹出节点时这并无问题,但在分割和合并列表时确实会有影响。 149 | 150 | 考虑在两种布局里分别分割一个列表: 151 | 152 | ```text 153 | 布局 1: 154 | 155 | [Elem A, ptr] -> (Elem B, ptr) -> (Elem C, ptr) -> (Empty *junk*) 156 | 157 | 在C点分割: 158 | 159 | [Elem A, ptr] -> (Elem B, ptr) -> (Empty *junk*) 160 | [Elem C, ptr] -> (Empty *junk*) 161 | ``` 162 | 163 | ```text 164 | 布局 2: 165 | 166 | [ptr] -> (Elem A, ptr) -> (Elem B, ptr) -> (Elem C, *null*) 167 | 168 | 在C点分割: 169 | 170 | [ptr] -> (Elem A, ptr) -> (Elem B, *null*) 171 | [ptr] -> (Elem C, *null*) 172 | ``` 173 | 174 | 布局2的分割仅仅涉及将B的指针拷贝到栈上,并把原值设置为null。布局1最终还是做了同一件事,但是还得把C从堆中拷贝到栈中。反过来执行上述操作,就是合并列表。 175 | 176 | 链表的优点之一就是可以在节点中构建元素,然后在列表中随意调换它的位置而不需移动它的内存。你只需要调整指针,元素就被“移动了”。第一个布局毁掉了这个特点。 177 | 178 | 好吧,我现在很确信布局1是糟糕的。我们要怎么重写List呢?可以这么做: 179 | 180 | ```rust 181 | pub enum List { 182 | Empty, 183 | ElemThenEmpty(i32), 184 | ElemThenNotEmpty(i32, Box), 185 | } 186 | ``` 187 | 188 | 或许你觉得这看起来更糟了。一个问题是,这让逻辑变得更复杂了。具体地说,现在出现了一个完全无效的状态:`ElemThenNotEmpty(0, Box(Empty))`。它也*仍*被内存分配模式不一致的问题所困扰。 189 | 190 | 不过它确实有*一个*有趣的特性:它完全避免了在堆里分配Empty,让堆内存分配的数量减少了1。不幸的是,这么做反而浪费了*更多空间*!因为之前的布局利用了*空指针优化*。 191 | 192 | 我们之前了解到每个enum需要存储一个*标签*,来指明它代表哪一个enum的*变体*。然而,如果我们有如下特殊类型的enum: 193 | 194 | ```rust 195 | enum Foo { 196 | A, 197 | B(ContainsANonNullPtr), 198 | } 199 | ``` 200 | 201 | 空指针优化就会发挥作用,*消除标签所占用的内存空间*。如果变体是A,那整个enum就被设置为0。否则,变体是B。这可以工作,因为B存放了一个非空指针,永远不可能为0。真聪明! 202 | 203 | 你还能想到能进行这种优化的enum和类型么?实际上有很多!这就是为什么Rust没有详细描述enum的内存布局。悲伤的是,现在实现的优化只有空指针优化——尽管它很重要!这意味着`&`, `&mut`, `Box`, `Rc`, `Arc`, `Vec`,以及其他一些Rust中的重要类型,在放到一个 `Option` 中时没有多余开销!(上面这些概念的大部分,我们在适当的时候都会接触到) 204 | 205 | 206 | 所以我们要如何避免多余垃圾,统一的分配内存,*并且*从空指针优化中获益呢?我们需要更好的将存储元素和分配列表这两个想法分开。要做到它,我们该像C语言看齐:struct! 207 | 208 | enum让我们定义了一种可以存放多个变体中的一个的类型,而struct则让我们定义可以同时存放多种元素的类型。让我们把List分成两个类型吧:一个List,和一个Node。 209 | 210 | 和之前一样,一个List要么是Empty,要么是一个元素跟着一个List。不过,要通过另外一种类型来表示“一个元素跟着一个List”,我们可以将Box提升到一个更理想的位置: 211 | 212 | ```rust 213 | struct Node { 214 | elem: i32, 215 | next: List, 216 | } 217 | 218 | pub enum List { 219 | Empty, 220 | More(Box), 221 | } 222 | ``` 223 | 224 | 让我们检查各个条目: 225 | 226 | * 列表末尾不分配多余垃圾:通过! 227 | * `enum` 享受美妙的空指针优化:通过! 228 | * 所有元素的内存分配一致:通过! 229 | 230 | 好的!我们创建的正是用来指明第一种内存布局(官方Rust文档中所建议的那种)有问题的第二种内存布局。 231 | 232 | ```text 233 | > cargo build 234 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 235 | src/first.rs:8:11: 8:18 error: private type in exported type signature 236 | src/first.rs:8 More(Box), 237 | ^~~~~~~ 238 | error: aborting due to previous error 239 | Could not compile `lists`. 240 | ``` 241 | 242 | :( 243 | 244 | Rust又对我们发飙了。我们将`List`标记为public(因为我们想让其他人使用它),却没有公开`Node`。问题在于,`enum`的内部是完全公开的,所以在其中包含内部类型是不允许的。我们可以让整个`Node`都成为公开的,但是通常在Rust中,我们倾向于让实现细节私有化。让我们把`List`改造成一个struct,这样我们就可以隐藏实现细节: 245 | 246 | 247 | ```rust 248 | pub struct List { 249 | head: Link, 250 | } 251 | 252 | enum Link { 253 | Empty, 254 | More(Box), 255 | } 256 | 257 | struct Node { 258 | elem: i32, 259 | next: Link, 260 | } 261 | ``` 262 | 263 | 因为`List`是一个单值的struct,它的大小和该值完全相同。零代价抽象超赞! 264 | 265 | ```text 266 | > cargo build 267 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 268 | src/first.rs:2:2: 2:15 warning: struct field is never used: `head`, #[warn(dead_code)] on by default 269 | src/first.rs:2 head: Link, 270 | ^~~~~~~~~~~~~ 271 | src/first.rs:6:2: 6:7 warning: variant is never used: `Empty`, #[warn(dead_code)] on by default 272 | src/first.rs:6 Empty, 273 | ^~~~~ 274 | src/first.rs:7:2: 7:20 warning: variant is never used: `More`, #[warn(dead_code)] on by default 275 | src/first.rs:7 More(Box), 276 | ^~~~~~~~~~~~~~~~~~ 277 | src/first.rs:11:2: 11:9 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 278 | src/first.rs:11 elem: i32, 279 | ^~~~~~~ 280 | src/first.rs:12:2: 12:15 warning: struct field is never used: `next`, #[warn(dead_code)] on by default 281 | src/first.rs:12 next: Link, 282 | ^~~~~~~~~~~~~ 283 | ``` 284 | 285 | 好吧,终于编译了!Rust非常生气,因为我们现在写的东西完全无用:我们从不使用`head`,并且因为它是私有的;使用我们库的人也无法使用它。进而Link和Node也毫无用处。让我们来解决它吧!为我们的List实现一些代码! 286 | -------------------------------------------------------------------------------- /first-new.md: -------------------------------------------------------------------------------- 1 | # 创建 2 | 3 | 我们使用`impl`语句块把实际代码关联到一个类型上: 4 | 5 | ```rust 6 | impl List { 7 | // TODO:填充代码 8 | } 9 | ``` 10 | 11 | 现在我们只需要了解实际编写代码的方法。在Rust我们像这样声明一个函数: 12 | 13 | ```rust 14 | fn foo(arg1: Type1, arg2: Type2) -> ReturnType { 15 | // body 16 | } 17 | ``` 18 | 19 | 我们想要的第一件事是*构造*一个列表的方法。由于我们需要隐藏实现细节,需要以函数的形式提供它。在Rust中,创建新对象的通常方法是实现一个`impl`块中的普通静态函数: 20 | 21 | ```rust 22 | impl List { 23 | pub fn new() -> Self { 24 | List { head: Link::Empty } 25 | } 26 | } 27 | ``` 28 | 29 | 一些关键点: 30 | 31 | * Self是“我写在impl右侧的那个类型”的别名。不用重复真是太好了! 32 | * 创建一个struct的实例的语法和声明struct的语法基本相同,只是我们在每个字段的后面提供的是值而非它的类型。 33 | * 我们使用命名空间运算符`::`来访问enum的变体。 34 | * 函数的最后一个表达式被隐式的返回。这让简单的函数看起来更简洁。你仍然可以像其他C系语言一样,用`return`来提前返回。 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /first-ownership.md: -------------------------------------------------------------------------------- 1 | # 所有权入门 2 | 3 | 在我们能够创建一个列表之后,自然会想用它进行实际操作。我们使用“普通”(非静态)方法来实现这一点。方法在Rust中是一种特殊的函数,它的第一个参数是self,并且没有类型声明: 4 | 5 | ```rust,ignore 6 | fn foo(self, arg2: Type2) -> ReturnType { 7 | // body 8 | } 9 | ``` 10 | 11 | 主要存在3种self可以采用的形式:`self`, `&mut self` 和 `&self`。它们代表了Rust中所有权的三种主要形式: 12 | 13 | * `self` - 值(value) 14 | * `&mut self` - 可变引用(mutable reference) 15 | * `&self` - 共享引用(shared reference) 16 | 17 | 一个值代表了*真正的*所有权。你可以对值做你想做的任何事:移动它,销毁它,改变它的内容,或者通过一个引用借出它。当你通过值传递东西时,它就被*移动*到了新的位置。这个新位置现在拥有了这个值,并且老位置不能再访问该值。因此,对于大部分函数我们都不想使用`self`——如果调用函数让我们无法再访问它,那还真是很糟糕啊。 18 | 19 | 一个可变引用代表了对你不拥有的一个值的临时*唯一访问*权。当你拥有一个可变引用时,你被允许做几乎任何想做的事,只要满足该引用过期时,被借用者仍然可以看见合法的值。这意味着你可以完全覆盖这个值。一个有用的特殊情况是把这个值和另外一个做交换——我们会经常使用这一技巧。唯一不能对`&mut`做的一件事是不加替换的将它的值移出。对于要对`self`加以修改的方法,`&mut self`是极好的。 20 | 21 | 一个共享引用代表对你不拥有的值的临时*共享访问*。由于访问是共享的,通常改变任何内容是不被允许的。可以把`&`想作把值丢到博物馆里用于展览。如果我们只想观察`self`的值,`&`是很好用的。 22 | 23 | 晚些我们会见到在一些特殊情况下,允许进行值的修改的规则。这就是为什么共享引用不被称为*不可变引用*。实际上,可变引用还可以称为*唯一引用*,但是我们发现在99%的情况下,将可变性和所有权联系起来可以带来正确的直觉。 24 | 25 | -------------------------------------------------------------------------------- /first-pop.md: -------------------------------------------------------------------------------- 1 | # Pop 2 | 3 | 和`push`一样,`pop`想要改变列表;除此之外,我们还想返回结果。然而`pop`还得处理一个特殊的边界情况:如果列表是空的呢?为了表示这个情况,我们使用可靠的`Option`类型: 4 | 5 | ```rust 6 | pub fn pop(&mut self) -> Option { 7 | //TODO 8 | } 9 | ``` 10 | 11 | `Option`是一个表示一个值可能存在也可能不存在的enum。它要么是`Some(T)`,要么是`None`。我们也可以像Link一样创建一个自己的enum,但是我们想让用户了解我们的返回类型到底是什么,而Option是如此的无处不在,每个人都知道它。实际上,因为它是如此的基本,它被隐式的导入到了每一个源文件的作用域中,也包括它的两个变体:`Some`和`None`(这样我们就不用写`Option::None`)。 12 | 13 | 在`Option`尖括号里的部分指出Option实际上是一个泛型,它的泛型参数是T。这意味着你可以创建一个任何类型的Option! 14 | 15 | 所以,我们有这个`Link`了,我们怎么检查它是Empty还是More呢?使用`match`进行模式匹配: 16 | 17 | ```rust 18 | pub fn pop(&mut self) -> Option { 19 | match self.head { 20 | Link::Empty => { 21 | // TODO 22 | } 23 | Link::More(node) => { 24 | // TODO 25 | } 26 | }; 27 | } 28 | ``` 29 | 30 | ```text 31 | > cargo build 32 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 33 | src/first.rs:27:2: 36:3 error: not all control paths return a value [E0269] 34 | src/first.rs:27 pub fn pop(&mut self) -> Option { 35 | src/first.rs:28 match self.head { 36 | src/first.rs:29 Link::Empty => { 37 | src/first.rs:30 // TODO 38 | src/first.rs:31 } 39 | src/first.rs:32 Link::More(node) => { 40 | ... 41 | error: aborting due to previous error 42 | Could not compile `lists`. 43 | ``` 44 | 45 | 啊,`pop`必须返回一个值,我们还没做这件事。我们*可以*直接返回`None`,但是在这情况下,返回`unimplemented!`来指出我们没有完成该函数的实现会更好。`unimplemented!`是一个宏(`!代表一个宏`),它会在被调用的时候让整个程序panic(基本上也就是以可控的方式崩溃)。 46 | 47 | ```rust 48 | pub fn pop(&mut self) -> Option { 49 | match self.head { 50 | Link::Empty => { 51 | // TODO 52 | } 53 | Link::More(node) => { 54 | // TODO 55 | } 56 | }; 57 | unimplemented!() 58 | } 59 | ``` 60 | 61 | 无条件panic是一个[发散函数(diverging function)][diverging]的例子。发散函数永远不会返回到调用者,所以无论一个地方期待何种类型的返回值,它的返回值都能拿来用。在这里,`unimplemented!`被使用在期待一个`Option`的地方。 62 | 63 | 注意到我们不需要在程序里写`return`。函数中的最后一个表达式也就隐式的成为它的返回值。这让我们可以更精炼的表达简单的逻辑。你也可以像C系语言一样,显式的`return`返回。 64 | 65 | ```text 66 | > cargo build 67 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 68 | src/first.rs:28:9: 28:13 error: cannot move out of borrowed content 69 | src/first.rs:28 match self.head { 70 | ^~~~ 71 | src/first.rs:32:15: 32:19 note: attempting to move value to here 72 | src/first.rs:32 Link::More(node) => { 73 | ^~~~ 74 | src/first.rs:32:15: 32:19 help: to prevent the move, use `ref node` or `ref mut node` to capture value by reference 75 | error: aborting due to previous error 76 | Could not compile `lists`. 77 | ``` 78 | 79 | 噢,Rust,别纠缠不休了!和往常一样,Rust对我们非常生气。值得感谢的是,这次它还给出了深入的信息! 80 | 81 | ```text 82 | src/first.rs:17:9: 17:13 error: cannot move out of borrowed content 83 | src/first.rs:17 match self.head { 84 | ^~~~ 85 | src/first.rs:21:15: 21:19 note: attempting to move value to here 86 | src/first.rs:21 Link::More(node) => { 87 | ^~~~ 88 | ``` 89 | 90 | 模式匹配默认会移动匹配的值,所以这就是它如此生气的原因。 91 | 92 | ```text 93 | help: to prevent the move, use `ref node` or `ref mut node` to capture value by reference 94 | 帮助:要避免移动,使用`ref node`或`ref mut node`来引用捕获该值 95 | ``` 96 | 97 | 为了避免移动,我们使用`ref`关键词来指明我们想要把`node`进行引用绑定。来试试吧: 98 | 99 | ```rust 100 | pub fn pop(&mut self) -> Option { 101 | match self.head { 102 | Link::Empty => { 103 | // TODO 104 | } 105 | Link::More(ref node) => { 106 | // TODO 107 | } 108 | }; 109 | unimplemented!() 110 | } 111 | ``` 112 | 113 | ```text 114 | > cargo build 115 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 116 | src/first.rs:13:2: 13:9 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 117 | src/first.rs:13 elem: i32, 118 | ^~~~~~~ 119 | src/first.rs:14:2: 14:15 warning: struct field is never used: `next`, #[warn(dead_code)] on by default 120 | src/first.rs:14 next: Link, 121 | ^~~~~~~~~~~~~ 122 | src/first.rs:32:15: 32:23 warning: unused variable: `node`, #[warn(unused_variables)] on by default 123 | src/first.rs:32 Link::More(ref node) => { 124 | ^~~~~~~~ 125 | ``` 126 | 127 | 好耶,又编译了!现在让我们搞清楚实现逻辑。我们要创建一个Option,所以要为这个预留一个变量。在Empty情况下要返回None,在More情况下需要返回`Some(i32)`,并且改变列表的head。来吧: 128 | 129 | ```rust 130 | pub fn pop(&mut self) -> Option { 131 | let result; 132 | match self.head { 133 | Link::Empty => { 134 | result = None; 135 | } 136 | Link::More(ref node) => { 137 | result = Some(node.elem); 138 | self.head = node.next; 139 | } 140 | }; 141 | result 142 | } 143 | ``` 144 | 145 | ```text 146 | > cargo build 147 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 148 | src/first.rs:39:29: 39:33 error: cannot move out of borrowed content 149 | src/first.rs:39 self.head = node.next; 150 | ^~~~ 151 | src/first.rs:39:17: 39:38 error: cannot assign to `self.head` because it is borrowed 152 | src/first.rs:39 self.head = node.next; 153 | ^~~~~~~~~~~~~~~~~~~~~ 154 | src/first.rs:37:24: 37:32 note: borrow of `self.head` occurs here 155 | src/first.rs:37 Link::More(ref node) => { 156 | ^~~~~~~~ 157 | error: aborting due to 2 previous errors 158 | Could not compile `lists`. 159 | ``` 160 | 161 | *头* 162 | 163 | *桌* 164 | 165 | 我们现在有了两个不同的的错误。。首先,我们在仅仅拥有一个共享引用的情况下就尝试把值移动出`node`。其次,在我们已经租借了`node`的引用的时候,还在尝试改变`self.head`的值! 166 | 167 | 真是一堆纠缠不清的乱东西。 168 | 169 | 我们应该后退一步,思考我们要做什么。我们想要: 170 | 171 | * 检查列表是否为空。 172 | * 如果是空的,返回None 173 | * 如果是非空 174 | * 移除list头部 175 | * 移除该头部的`elem` 176 | * 将列表的head替换为`next` 177 | * 返回`Some(elem)` 178 | 179 | 重要的一点事我们想要*删除*东西,这意味着我们需要*按值*获取list的head。我们肯定不能通过由`ref node`获取的共享引用来做这件事。我们也“只”拥有一个可变引用,所以能移动东西的唯一方法就是*替换它*。看来我们又在做Empty替换那一套了!来试试吧: 180 | 181 | ```rust 182 | pub fn pop(&mut self) -> Option { 183 | let result; 184 | match mem::replace(&mut self.head, Link::Empty) { 185 | Link::Empty => { 186 | result = None; 187 | } 188 | Link::More(node) => { 189 | result = Some(node.elem); 190 | self.head = node.next; 191 | } 192 | }; 193 | result 194 | } 195 | ``` 196 | 197 | ```text 198 | cargo build 199 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 200 | ``` 201 | 202 | 我 的 天 哪 203 | 204 | 它编译了,一个警告都没有!!!!! 205 | 206 | 这里我要给出我的优化提示了:我们现在返回的是result变量的值,但实际上根本不用这么做!就像一个函数的结果是它的最后一个表达式,每个代码块的结果也是它的最后一个表达式。通常我们使用分号来阻止这一行为,这会让代码块的值变成空元组(tuple)`()`。这实际上也是不声明返回值的函数——例如`push`——返回的。 207 | 208 | 因此,我们可以将`pop`修改为: 209 | 210 | ```rust 211 | pub fn pop(&mut self) -> Option { 212 | match mem::replace(&mut self.head, Link::Empty) { 213 | Link::Empty => None, 214 | Link::More(node) => { 215 | self.head = node.next; 216 | Some(node.elem) 217 | } 218 | } 219 | } 220 | ``` 221 | 222 | 这更简洁,也更符合语言惯例。注意到Link::Empty分支只需要求值一个表达式,所以我们把大括号也去掉了。这是对于简单情况的简便处理。 223 | 224 | ```text 225 | > cargo build 226 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 227 | src/first.rs:36:22: 36:31 error: use of moved value: `node` [E0382] 228 | src/first.rs:36 Some(node.elem) 229 | ^~~~~~~~~ 230 | src/first.rs:35:29: 35:38 note: `node` moved here (through moving `node.next`) because it has type `first::Link`, which is non-copyable 231 | src/first.rs:35 self.head = node.next; 232 | ^~~~~~~~~ 233 | error: aborting due to previous error 234 | ``` 235 | 236 | 啥?别这样啊。 237 | 238 | 为什么我们的代码不工作了?! 239 | 240 | 实际上,我们之前的代码只是侥幸通过了编译,借助Copy的魔法。当我们介绍[所有权][ownership]的时候说过,当你移动值的时候,就无法再使用它。对于某些类型,这是完全合理的。我们的好朋友Box为我们管理堆中的内存分配,而我们显然不想让两段代码认为它们应该释放相同的一块内存。 241 | 242 | 但是对某些类型这简直*糟透了*。整数可没有所有权语义:它们只是毫无意义的数字!这也正是为什么整数被标记为Copy。Copy类型可以通过按位复制进行完整的拷贝。因此,它们拥有一个超能力:当被移动的时候,老的值仍然是可用的。作为结果,你可以将一个Copy类型从引用移出而不需替换! 243 | 244 | 所有rust中的基本数字类型(i32, u64, bool, f32, char, etc...)都是Copy。同时,共享引用也是Copy,这很有用!只要一个自定类型的所有字段都是Copy,你也可以将该类型声明为Copy。 245 | 246 | 总之,回到代码:出了什么错?在第一次迭代过程中,我们对result进行赋值时在进行*拷贝*,因此node没被改变,可以被继续用于下一个操作。现在我们在移动`next`(它不是Copy),而这在我们能碰到elem之前消耗掉了整个Box里的值。 247 | 248 | 现在,我们可以重新调整代码来只拿到`elem`,但我们只是使用i32作为某种数据的占位符。晚些时候我们会处理非Copy类型的数据,所以最好现在就研究研究怎么做。 249 | 250 | 正确的答案是将*整个*节点从Box中取出来,这样就可以安全的将它拆开了。我们通过显式解引用操作来做这件事: 251 | 252 | ```rust 253 | pub fn pop(&mut self) -> Option { 254 | match mem::replace(&mut self.head, Link::Empty) { 255 | Link::Empty => None, 256 | Link::More(boxed_node) => { 257 | let node = *boxed_node; 258 | self.head = node.next; 259 | Some(node.elem) 260 | } 261 | } 262 | } 263 | ``` 264 | 265 | 在这之后,Rust就可以足够好的理解一个栈上的值,来让你一步步分解它了。 266 | 267 | ```text 268 | > cargo build 269 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 270 | ``` 271 | 272 | 不错。 273 | 274 | Box在Rust里真的很特殊,因为它是固定于语言里的一部分,编译器可以让你对它做一些其它任何类型都不能做的事。我们实际上一直在做这件事:`DerefMove`。当你拥有一个指针类型时,可以通过`*`或`.`来获得它的内容。通常你可以获得一个`Deref`或者一个`DerefMut`,分别对应共享和可变引用。 275 | 276 | 但是因为Box完全拥有它的内容,你可以通过解引用*将内容移出*。这是完完全全的魔法,因为其他任何类型都无法实现这个操作。编译器还知道如何在Box上实现很多很多的酷炫技巧,只因为它*就是*Box,但是它们都被阻挡在了1.0版本的实现目标之外,等待进一步的设计。理想的,Box会在未来完全可自定义化。 277 | 278 | 279 | 280 | [ownership]: first-ownership.html 281 | [diverging]: http://doc.rust-lang.org/nightly/book/functions.html#diverging-functions 282 | -------------------------------------------------------------------------------- /first-push.md: -------------------------------------------------------------------------------- 1 | # Push 2 | 3 | 我们来实现将一个值推入列表的功能吧。`push`*改变了*列表,因此我们需要`&mut self`。还需要提供一个推入的i32参数: 4 | 5 | ```rust 6 | impl List { 7 | pub fn push(&mut self, elem: i32) { 8 | // TODO 9 | } 10 | } 11 | ``` 12 | 13 | 首先,我们需要创建一个节点来存储我们的元素: 14 | 15 | ```rust 16 | pub fn push(&mut self, elem: i32) { 17 | let new_node = Node { 18 | elem: elem, 19 | next: ????? 20 | }; 21 | } 22 | ``` 23 | 24 | `next`那里存储的是什么呢?呃,是整个原先的列表!我们...可以这么做么? 25 | 26 | ```rust 27 | impl List { 28 | pub fn push(&mut self, elem: i32) { 29 | let new_node = Node { 30 | elem: elem, 31 | next: self.head, 32 | }; 33 | } 34 | } 35 | ``` 36 | 37 | ```text 38 | > cargo build 39 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 40 | src/first.rs:19:10: 19:14 error: cannot move out of borrowed content 41 | src/first.rs:19 next: self.head, 42 | ^~~~ 43 | error: aborting due to previous error 44 | Could not compile `lists`. 45 | ``` 46 | 47 | 不不不。Rust在告诉我们正确的事,但是它的实际意义和修复它的方法并不明了: 48 | 49 | > cannot move out of borrowed content (无法移动出租借的内容) 50 | 51 | 我们尝试把`self.head`字段移动到`next`中,但是Rust不想让我们做这件事。这会导致在我们的租借结束,值返回到其所有者的时候,`self`只是部分初始化的。正如我们之前所说,这是你不能通过`&mut`做的*唯一一件事*:这样做是非常粗野的,而Rust则打算尽量保持礼貌(同时这也是极其危险的,但是并不是Rust关心它的原因)。 52 | 53 | 如果我们把东西放回去呢?具体的说,就是我们创建的节点: 54 | 55 | 56 | ```rust 57 | pub fn push(&mut self, elem: i32) { 58 | let new_node = Box::new(Node { 59 | elem: elem, 60 | next: self.head, 61 | }); 62 | 63 | self.head = Link::More(new_node); 64 | } 65 | ``` 66 | 67 | ```text 68 | > cargo build 69 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 70 | src/first.rs:19:10: 19:14 error: cannot move out of borrowed content 71 | src/first.rs:19 next: self.head, 72 | ^~~~ 73 | error: aborting due to previous error 74 | Could not compile `lists`. 75 | ``` 76 | 77 | 不行。在原则上,Rust是可以接受这样的行为的,但是它不会(出于数个原因——最重要的是安全性)我们需要某种方法得到head,而不让Rust发现它已经消失了。我们转而向声名狼藉的Rust黑客Indiana Jones寻求建议: 78 | 79 | ![Indy准备进行mem::replace](indy.gif) 80 | 81 | 噢没错,Indy建议采用`mem::replace`这一招。这个极其有用的函数让我们通过*替换*将一个值从租借中偷出来。让我们先在文件顶部把`std::mem`拉进来,这样`mem`就在局部作用域了: 82 | 83 | ```rust 84 | use std::mem; 85 | ``` 86 | 87 | 然后恰当的使用它: 88 | 89 | ```rust 90 | pub fn push(&mut self, elem: i32) { 91 | let new_node = Box::new(Node { 92 | elem: elem, 93 | next: mem::replace(&mut self.head, Link::Empty), 94 | }); 95 | 96 | self.head = Link::More(new_node); 97 | } 98 | ``` 99 | 100 | 在将self.head替换为列表的新头部之前,我们临时的将它`replace`为Link::Empty。我不会撒谎:非要这么去做真是很糟糕。悲伤的是,我们必须这样(至少现在)。 101 | 102 | 不过,这样子`push`就完成了!或许吧。说真的,我们应该测试一下它。现在来说,最简单的测试方法是实现`pop`,然后确认它给出了正确的结果。 103 | 104 | -------------------------------------------------------------------------------- /first-test.md: -------------------------------------------------------------------------------- 1 | # 测试 2 | 3 | 我们现在已经实现了`push`和`pop`,就可以测试我们的栈了!Rust和cargo把测试作为一个一级特性来实现,所以写起测试来会很轻松。我们需要做的只是写一个函数,然后用`#[test]`标记它。 4 | 5 | 通常在Rust社区中,我们会把测试代码放在它所测试的部分的附近。不过我们通常会为测试创建单独的命名空间,来让它不与“真正的”代码产生冲突。就像我们用`mod`来表明`first.rs`应该被包含在`lib.rs`中,可以使用`mod`来*内联的*创建一个新文件: 6 | 7 | 8 | ```rust 9 | // in first.rs 10 | 11 | mod test { 12 | #[test] 13 | fn basics() { 14 | // TODO 15 | } 16 | } 17 | ``` 18 | 19 | 之后,我们通过`cargo test`调用它。 20 | 21 | ```text 22 | > cargo test 23 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 24 | Running target/debug/lists-5c71138492ad4b4a 25 | 26 | running 1 test 27 | test first::test::basics ... ok 28 | 29 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured 30 | 31 | Doc-tests lists 32 | 33 | running 0 tests 34 | 35 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 36 | ``` 37 | 38 | 好的,我们的什么都不做的测试通过了!我们来让它实际做些事吧。我们会使用`assert_eq!`宏来进行测试。这不是什么特殊的测试魔法。它所做的仅仅是比较你给它的两个值,并且让在它们不相等的情况下让程序panic。没错,你通过崩溃来指出测试中的失败! 39 | 40 | ```rust 41 | mod test { 42 | #[test] 43 | fn basics() { 44 | let mut list = List::new(); 45 | 46 | // 检查空列表行为正确 47 | assert_eq!(list.pop(), None); 48 | 49 | // 填充列表 50 | list.push(1); 51 | list.push(2); 52 | list.push(3); 53 | 54 | // 检查通常的移除 55 | assert_eq!(list.pop(), Some(3)); 56 | assert_eq!(list.pop(), Some(2)); 57 | 58 | // 推入更多元素来确认没有问题 59 | list.push(4); 60 | list.push(5); 61 | 62 | // 检查通常的移除 63 | assert_eq!(list.pop(), Some(5)); 64 | assert_eq!(list.pop(), Some(4)); 65 | 66 | // 检查完全移除 67 | assert_eq!(list.pop(), Some(1)); 68 | assert_eq!(list.pop(), None); 69 | } 70 | } 71 | ``` 72 | 73 | ```text 74 | > cargo test 75 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 76 | src/first.rs:47:24: 47:33 error: failed to resolve. Use of undeclared type or module `List` [E0433] 77 | src/first.rs:47 let mut list = List::new(); 78 | ^~~~~~~~~ 79 | src/first.rs:47:24: 47:33 error: unresolved name `List::new` [E0425] 80 | src/first.rs:47 let mut list = List::new(); 81 | ^~~~~~~~~ 82 | error: aborting due to 2 previous errors 83 | ``` 84 | 85 | 噢!因为我们做了一个新的模块,所以需要把List显式的导入进来才能使用它。 86 | 87 | ```rust 88 | mod test { 89 | use super::List; 90 | // 其他不变 91 | } 92 | ``` 93 | 94 | ```text 95 | > cargo test 96 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 97 | src/first.rs:45:9: 45:20 warning: unused import, #[warn(unused_imports)] on by default 98 | src/first.rs:45 use super::List; 99 | ^~~~~~~~~~~ 100 | Running target/debug/lists-5c71138492ad4b4a 101 | 102 | running 1 test 103 | test first::test::basics ... ok 104 | 105 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured 106 | 107 | Doc-tests lists 108 | 109 | running 0 tests 110 | 111 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 112 | ``` 113 | 114 | 好极了! 115 | 116 | 那个警告又是怎么回事呢……?我们清楚的在测试里用了List! 117 | 118 | ……但仅仅在测试的过程中!为了平息编译器(以及对库的使用者友好),我们应该指明test模块只会在运行测试的过程中编译。 119 | 120 | ``` 121 | #[cfg(test)] 122 | mod test { 123 | use super::List; 124 | // everything else the same 125 | } 126 | ``` 127 | 128 | 这就是关于测试的所有要点了! 129 | 130 | -------------------------------------------------------------------------------- /first.md: -------------------------------------------------------------------------------- 1 | # 一个糟糕的单向链表栈 2 | 3 | 这将是*到此为止*最长的教程,因为我们需要基本上介绍Rust的所有内容,并且需要通过“强硬”的构建一些东西来更好的了解这门语言。 4 | 5 | 我们会把第一个列表放在 `src/first.rs` 中。我们需要告诉Rust `first.rs`是我们的库使用的文件。只需要把下述代码放在 `src/lib.rs` 即可(Cargo已经为我们创建了): 6 | 7 | ```rust 8 | // in lib.rs 9 | pub mod first; 10 | ``` 11 | 12 | -------------------------------------------------------------------------------- /fourth-breaking.md: -------------------------------------------------------------------------------- 1 | % Breaking Down 2 | 3 | `pop_front` should be the same basic logic as `push_front`, but backwards. Let's 4 | try: 5 | 6 | ```rust 7 | pub fn pop_front(&mut self) -> Option { 8 | // need to take the old head, ensuring it's -2 9 | self.head.take().map(|old_head| { // -1 old 10 | match old_head.borrow_mut().next.take() { 11 | Some(new_head) => { // -1 new 12 | // not emptying list 13 | new_head.borrow_mut().prev.take(); // -1 old 14 | self.head = Some(new_head); // +1 new 15 | // total: -2 old, +0 new 16 | } 17 | None => { 18 | // emptying list 19 | self.tail.take(); // -1 old 20 | // total: -2 old, (no new) 21 | } 22 | } 23 | old_head.elem 24 | }) 25 | } 26 | ``` 27 | 28 | ```text 29 | > cargo build 30 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 31 | src/fourth.rs:63:13: 63:26 error: attempted access of field `elem` on type `alloc::rc::Rc>>`, but no field with that name was found 32 | src/fourth.rs:63 old_head.elem 33 | ^~~~~~~~~~~~~ 34 | note: in expansion of closure expansion 35 | src/fourth.rs:49:30: 64:10 note: expansion site 36 | error: aborting due to previous error 37 | Could not compile `lists`. 38 | ``` 39 | 40 | ACK. *RefCells*. Gotta `borrow_mut` again I guess... 41 | 42 | ```rust 43 | pub fn pop_front(&mut self) -> Option { 44 | self.head.take().map(|old_head| { 45 | match old_head.borrow_mut().next.take() { 46 | Some(new_head) => { 47 | new_head.borrow_mut().prev.take(); 48 | self.head = Some(new_head); 49 | } 50 | None => { 51 | self.tail.take(); 52 | } 53 | } 54 | old_head.borrow_mut().elem 55 | }) 56 | } 57 | ``` 58 | 59 | ```text 60 | cargo build 61 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 62 | src/fourth.rs:58:13: 58:34 error: cannot move out of borrowed content 63 | src/fourth.rs:58 old_head.borrow_mut().elem 64 | ^~~~~~~~~~~~~~~~~~~~~ 65 | note: in expansion of closure expansion 66 | src/fourth.rs:48:30: 59:10 note: expansion site 67 | error: aborting due to previous error 68 | Could not compile `lists`. 69 | ``` 70 | 71 | *sigh* 72 | 73 | > cannot move out of borrowed content 74 | 75 | Hrm... It seems that Box was *really* spoiling us. `borrow_mut` only gets us 76 | an `&mut Node`, but we can't move out of that! 77 | 78 | We need something that takes a `RefCell` and gives us a `T`. Let's check 79 | [the docs][refcell] for something like that: 80 | 81 | > `fn into_inner(self) -> T` 82 | > 83 | > Consumes the RefCell, returning the wrapped value. 84 | 85 | That looks promising! 86 | 87 | ```rust 88 | old_head.into_inner().elem 89 | ``` 90 | 91 | ```text 92 | > cargo build 93 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 94 | src/fourth.rs:58:13: 58:21 error: cannot move out of borrowed content 95 | src/fourth.rs:58 old_head.into_inner().elem 96 | ^~~~~~~~ 97 | note: in expansion of closure expansion 98 | src/fourth.rs:48:30: 59:10 note: expansion site 99 | error: aborting due to previous error 100 | Could not compile `lists`. 101 | ``` 102 | 103 | Uh... what? Nothing changed! Dang compiler! 104 | 105 | It's actually telling us the right thing, but it's not giving us quite enough 106 | information to figure it out. So I'm just going to cut the chase: where 107 | previously we were trying to move a `T` out of the `&mut Node`, now we're 108 | trying to move a `RefCell` out of an `&RefCell`. We've just pushed the 109 | problem up the ownership chain. 110 | 111 | As we saw in the previous chapter, `Rc` only lets us get shared references 112 | into it's internals. That makes sense, because that's *the whole point* of 113 | reference counted pointers: they're shared! 114 | 115 | This was a problem for us when we wanted to implement Drop for our reference 116 | counted list, because we needed to gain ownership of the nodes to drop them 117 | one at a time. We saw that only Rust's nightlies expose the tools to do this. 118 | 119 | ------ 120 | 121 | So... we're going to use nightly! 122 | 123 | My preferred way to use nightly is to use Brian Anderson's *amazing* [multirust]. 124 | It lets you easily update and switch between Rust's stable, beta, and nightly 125 | channels. Or even make up your own custom toolchains! This is a total godsend 126 | for me as someone who maintains several libraries which bridge the 127 | stable-nightly gap. It also lets me easily test out custom builds of the 128 | compiler and standard library when I'm working on those! 129 | 130 | Sadly, its current design doesn't work on Windows. So if you're on Windows or 131 | otherwise *hate amazing things*. You'll need to grab a nightly from 132 | [the rust-lang website][downloads]. 133 | 134 | ------ 135 | 136 | Alright, got the nightly? Great! 137 | 138 | Now we can use `Rc::try_unwrap`, which moves out the contents of an Rc out 139 | if its refcount is 1. 140 | 141 | ```rust 142 | Rc::try_unwrap(old_head).unwrap().into_inner().elem 143 | ``` 144 | 145 | `Rc::try_unwrap` returns a `Result>`. Results are basically a 146 | generalized `Option`, where the `None` case has data associated with it. In 147 | this case, the `Rc` you tried to unwrap. Since we don't care about the case 148 | where it fails (if we wrote our program correctly, it *has* to succeed), we 149 | just call `unwrap` on it. 150 | 151 | `unwrap` is a function on Results and Options that basically says "get the 152 | the value out, or panic the program". Panics are A Whole Thing, so I'll simplify 153 | things by just saying a panics crash your program in a controlled manner, 154 | effectively making every function instantly return. Notably, it makes sure to 155 | call all the destructors that happen to be laying around on the way out. So your 156 | memory will get freed (meh? OS does that anyway, right?) and your Connections 157 | and Files will get properly saved/closed (yay!). 158 | 159 | This is a bit of a lie, there's cases where you can stop a panic. In particular 160 | at a thread boundary if you're into that whole "parallelism" thing. That whole 161 | freeing your memory thing is less lame there, *I guess*. 162 | 163 | Anyway, let's see what compiler error we get next (let's face it, there's going 164 | to be one). 165 | 166 | ```text 167 | > cargo build 168 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 169 | src/fourth.rs:58:38: 58:46 error: no method named `unwrap` found for type `core::result::Result>, alloc::rc::Rc>>>` in the current scope 170 | src/fourth.rs:58 Rc::try_unwrap(old_head).unwrap().into_inner().elem 171 | ^~~~~~~~ 172 | note: in expansion of closure expansion 173 | src/fourth.rs:48:30: 59:10 note: expansion site 174 | src/fourth.rs:58:38: 58:46 note: the method `unwrap` exists but the following trait bounds were not satisfied: `alloc::rc::Rc>> : core::fmt::Debug` 175 | error: aborting due to previous error 176 | Could not compile `lists`. 177 | ``` 178 | 179 | UGH. This is dumb. Rust is dumb. `unwrap` on Result requires that you can 180 | debug-print the error case. `RefCell` only implements `Debug` if `T` does. 181 | `Node` doesn't implement Debug. We could just implement Debug for Node but I'm 182 | not in the mood. Let's just work around this by turning the `Result` into an 183 | `Option` with the `ok` method: 184 | 185 | ```rust 186 | Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem 187 | ``` 188 | 189 | PLEASE. 190 | 191 | ```text 192 | cargo build 193 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 194 | src/fourth.rs:58:13: 58:27 error: use of unstable library feature 'rc_unique' 195 | src/fourth.rs:58 Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem 196 | ^~~~~~~~~~~~~~ 197 | note: in expansion of closure expansion 198 | src/fourth.rs:48:30: 59:10 note: expansion site 199 | src/fourth.rs:58:13: 58:27 help: add #![feature(rc_unique)] to the crate attributes to enable 200 | error: aborting due to previous error 201 | Could not compile `lists`. 202 | ``` 203 | 204 | NO. 205 | 206 | > add `#![feature(rc_unique)]` to the crate attributes to enable 207 | 208 | FINE. 209 | 210 | ```rust 211 | // in lib.rs 212 | #![feature(rc_unique)] 213 | 214 | pub mod first; 215 | pub mod second; 216 | pub mod third; 217 | pub mod fourth; 218 | pub mod fifth; 219 | ``` 220 | 221 | ```text 222 | cargo build 223 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 224 | ``` 225 | 226 | YES. 227 | 228 | *phew* 229 | 230 | Ok what was that last thing? Rust *really* doesn't want you using unstable 231 | features, so it makes you explicitly name every single one you do at the top 232 | of your crate. *shrug* 233 | 234 | As one of the people who makes these decisions, I can just say that `try_unwrap` 235 | is totally going to be stabilized as-is. It's just mixed in with some other 236 | annoying details that we're hashing out. 237 | 238 | But we did it. 239 | 240 | We implemented `push` and `pop`. 241 | 242 | Let's test by stealing the old `stack` basic test (because that's all that 243 | we've implemented so far): 244 | 245 | ``` 246 | #[cfg(test)] 247 | mod test { 248 | use super::List; 249 | 250 | #[test] 251 | fn basics() { 252 | let mut list = List::new(); 253 | 254 | // Check empty list behaves right 255 | assert_eq!(list.pop_front(), None); 256 | 257 | // Populate list 258 | list.push_front(1); 259 | list.push_front(2); 260 | list.push_front(3); 261 | 262 | // Check normal removal 263 | assert_eq!(list.pop_front(), Some(3)); 264 | assert_eq!(list.pop_front(), Some(2)); 265 | 266 | // Push some more just to make sure nothing's corrupted 267 | list.push_front(4); 268 | list.push_front(5); 269 | 270 | // Check normal removal 271 | assert_eq!(list.pop_front(), Some(5)); 272 | assert_eq!(list.pop_front(), Some(4)); 273 | 274 | // Check exhaustion 275 | assert_eq!(list.pop_front(), Some(1)); 276 | assert_eq!(list.pop_front(), None); 277 | } 278 | } 279 | ``` 280 | 281 | ```text 282 | cargo test 283 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 284 | Running target/debug/lists-5c71138492ad4b4a 285 | 286 | running 9 tests 287 | test first::test::basics ... ok 288 | test fourth::test::basics ... ok 289 | test second::test::iter_mut ... ok 290 | test second::test::basics ... ok 291 | test fifth::test::iter_mut ... ok 292 | test third::test::basics ... ok 293 | test second::test::iter ... ok 294 | test third::test::iter ... ok 295 | test second::test::into_iter ... ok 296 | 297 | test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured 298 | 299 | Doc-tests lists 300 | 301 | running 0 tests 302 | 303 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 304 | ``` 305 | 306 | *Nailed it*. 307 | 308 | Now that we can properly remove things from the list, we can implement Drop. 309 | Drop is a little more conceptually interesting this time around. Where 310 | previously we bothered to implement Drop for our stacks just to avoid unbounded 311 | recursion, now we need to implement Drop to get *anything* to happen at all. 312 | 313 | `Rc` can't deal with cycles. If there's a cycle, everything will keep everything 314 | else alive. A doubly linked list, as it turns out, is just a big chain of tiny 315 | cycles! So when we drop our list, the two end nodes will have their refcounts 316 | decremented down to 1... and then nothing else will happen. Well, if our list 317 | contains exactly one node we're good to go. But ideally a list should work right 318 | if it contains multiple elements. Maybe that's just me. 319 | 320 | As we saw, removing elements was a bit painful. So the easiest thing for us to 321 | do is just `pop` until we get None: 322 | 323 | ```rust 324 | impl Drop for List { 325 | fn drop(&mut self) { 326 | while self.pop_front().is_some() {} 327 | } 328 | } 329 | ``` 330 | 331 | ```text 332 | cargo build 333 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 334 | ``` 335 | 336 | (We actually could have done this with our mutable stacks, but shortcuts are for 337 | people who understand things!) 338 | 339 | We could look at implementing the `_back` versions of `push` and `pop`, but 340 | they're just copy-paste jobs which we'll defer to later in the chapter. For now 341 | let's look at more interesting things! 342 | 343 | 344 | [refcell]: https://doc.rust-lang.org/std/cell/struct.RefCell.html 345 | [multirust]: https://github.com/brson/multirust 346 | [downloads]: https://www.rust-lang.org/install.html 347 | -------------------------------------------------------------------------------- /fourth-building.md: -------------------------------------------------------------------------------- 1 | % Building Up 2 | 3 | Alright, we'll start with building the list. That's pretty straight-forward 4 | with this new system. `new` is still trivial, just None out all the fields. 5 | Also because it's getting a bit unwieldy, let's break out a Node constructor 6 | too: 7 | 8 | ```rust 9 | impl Node { 10 | fn new(elem: T) -> Rc> { 11 | Rc::new(RefCell::new(Node { 12 | elem: elem, 13 | prev: None, 14 | next: None, 15 | })) 16 | } 17 | } 18 | 19 | impl List { 20 | pub fn new() -> Self { 21 | List { head: None, tail: None } 22 | } 23 | } 24 | ``` 25 | 26 | ```text 27 | > cargo build 28 | 29 | **A BUNCH OF DEAD CODE WARNINGS BUT IT BUILT** 30 | ``` 31 | 32 | Yay! 33 | 34 | Now let's try to write pushing onto the front of the list. Because 35 | doubly-linked lists are signficantly more complicated, we're going to need 36 | to do a fair bit more work. Where singly-linked list operations could be 37 | reduced to an easy one-liner, doubly-linked list ops are fairly complicated. 38 | 39 | In particular we now need to specially handle some boundary cases around 40 | empty lists. Most operations will only touch the `head` or `tail` pointer. 41 | However when transitioning to or from the empty list, we need to edit 42 | *both* at once. 43 | 44 | An easy way for us to validate if our methods make sense is if we maintain 45 | the following invariant: each node should have exactly two pointers to it. 46 | Each node in the middle of the list is pointed at by its predecessor and 47 | successor, while the nodes on the ends are pointed to by the list itself. 48 | 49 | Let's take a crack at it: 50 | 51 | ```rust 52 | pub fn push_front(&mut self, elem: T) { 53 | // new node needs +2 links, everything else should be +0 54 | let new_head = Node::new(elem); 55 | match self.head.take() { 56 | Some(old_head) => { 57 | // non-empty list, need to connect the old_head 58 | old_head.prev = Some(new_head.clone()); // +1 new_head 59 | new_head.next = Some(old_head); // +1 old_head 60 | self.head = Some(new_head); // +1 new_head, -1 old_head 61 | // total: +2 new_head, +0 old_head -- OK! 62 | } 63 | None => { 64 | // empty list, need to set the tail 65 | self.tail = Some(new_head.clone()); // +1 new_head 66 | self.head = Some(new_head); // +1 new_head 67 | // total: +2 new_head -- OK! 68 | } 69 | } 70 | } 71 | ``` 72 | 73 | ```text 74 | cargo build 75 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 76 | src/fourth.rs:37:17: 37:30 error: attempted access of field `prev` on type `alloc::rc::Rc>>`, but no field with that name was found 77 | src/fourth.rs:37 old_head.prev = Some(new_head.clone()); 78 | ^~~~~~~~~~~~~ 79 | src/fourth.rs:38:17: 38:30 error: attempted access of field `next` on type `alloc::rc::Rc>>`, but no field with that name was found 80 | src/fourth.rs:38 new_head.next = Some(old_head); 81 | ^~~~~~~~~~~~~ 82 | error: aborting due to 2 previous errors 83 | Could not compile `lists`. 84 | ``` 85 | 86 | Alright. Compiler error. Good start. Good start. 87 | 88 | Why can't we access the `prev` and `next` fields on our nodes? It worked before 89 | when we just had an `Rc`. Seems like the `RefCell` is getting in the way. 90 | 91 | We should probably check the docs. 92 | 93 | *Google's "rust refcell"* 94 | 95 | *[clicks first link](https://doc.rust-lang.org/std/cell/struct.RefCell.html)* 96 | 97 | > A mutable memory location with dynamically checked borrow rules 98 | > 99 | > See the [module-level documentation](https://doc.rust-lang.org/std/cell/index.html) for more. 100 | 101 | *clicks link* 102 | 103 | > Shareable mutable containers. 104 | > 105 | > Values of the `Cell` and `RefCell` types may be mutated through shared references (i.e. 106 | > the common `&T` type), whereas most Rust types can only be mutated through unique (`&mut T`) 107 | > references. We say that `Cell` and `RefCell` provide 'interior mutability', in contrast 108 | > with typical Rust types that exhibit 'inherited mutability'. 109 | > 110 | > Cell types come in two flavors: `Cell` and `RefCell`. `Cell` provides `get` and `set` 111 | > methods that change the interior value with a single method call. `Cell` though is only 112 | > compatible with types that implement `Copy`. For other types, one must use the `RefCell` 113 | > type, acquiring a write lock before mutating. 114 | > 115 | > `RefCell` uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can 116 | > claim temporary, exclusive, mutable access to the inner value. Borrows for `RefCell`s are 117 | > tracked 'at runtime', unlike Rust's native reference types which are entirely tracked 118 | > statically, at compile time. Because `RefCell` borrows are dynamic it is possible to attempt 119 | > to borrow a value that is already mutably borrowed; when this happens it results in thread 120 | > panic. 121 | > 122 | > # When to choose interior mutability 123 | > 124 | > The more common inherited mutability, where one must have unique access to mutate a value, is 125 | > one of the key language elements that enables Rust to reason strongly about pointer aliasing, 126 | > statically preventing crash bugs. Because of that, inherited mutability is preferred, and 127 | > interior mutability is something of a last resort. Since cell types enable mutation where it 128 | > would otherwise be disallowed though, there are occasions when interior mutability might be 129 | > appropriate, or even *must* be used, e.g. 130 | > 131 | > * Introducing inherited mutability roots to shared types. 132 | > * Implementation details of logically-immutable methods. 133 | > * Mutating implementations of `Clone`. 134 | > 135 | > ## Introducing inherited mutability roots to shared types 136 | > 137 | > Shared smart pointer types, including `Rc` and `Arc`, provide containers that can be 138 | > cloned and shared between multiple parties. Because the contained values may be 139 | > multiply-aliased, they can only be borrowed as shared references, not mutable references. 140 | > Without cells it would be impossible to mutate data inside of shared boxes at all! 141 | > 142 | > It's very common then to put a `RefCell` inside shared pointer types to reintroduce 143 | > mutability: 144 | > 145 | > ```rust 146 | > use std::collections::HashMap; 147 | > use std::cell::RefCell; 148 | > use std::rc::Rc; 149 | > 150 | > fn main() { 151 | > let shared_map: Rc> = Rc::new(RefCell::new(HashMap::new())); 152 | > shared_map.borrow_mut().insert("africa", 92388); 153 | > shared_map.borrow_mut().insert("kyoto", 11837); 154 | > shared_map.borrow_mut().insert("piccadilly", 11826); 155 | > shared_map.borrow_mut().insert("marbles", 38); 156 | > } 157 | > ``` 158 | > 159 | > Note that this example uses `Rc` and not `Arc`. `RefCell`s are for single-threaded 160 | > scenarios. Consider using `Mutex` if you need shared mutability in a multi-threaded 161 | > situation. 162 | 163 | Hey, Rust's docs continue to be incredibly awesome. 164 | 165 | The meaty bit we care about is this line: 166 | 167 | ```rust 168 | shared_map.borrow_mut().insert("africa", 92388); 169 | ``` 170 | 171 | In particular, the `borrow_mut` thing. Seems we need to explicitly borrow a 172 | RefCell. The `.` operator's not going to do it for us. Weird. Let's try: 173 | 174 | ```rust 175 | pub fn push_front(&mut self, elem: T) { 176 | let new_head = Node::new(elem); 177 | match self.head.take() { 178 | Some(old_head) => { 179 | old_head.borrow_mut().prev = Some(new_head.clone()); 180 | new_head.borrow_mut().next = Some(old_head); 181 | self.head = Some(new_head); 182 | } 183 | None => { 184 | self.tail = Some(new_head.clone()); 185 | self.head = Some(new_head); 186 | } 187 | } 188 | } 189 | ``` 190 | 191 | 192 | ```text 193 | > cargo build 194 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 195 | src/fourth.rs:12:5: 12:12 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 196 | src/fourth.rs:12 elem: T, 197 | ^~~~~~~ 198 | ``` 199 | 200 | Hey, it built! Docs win again. 201 | -------------------------------------------------------------------------------- /fourth-final.md: -------------------------------------------------------------------------------- 1 | % Final Code 2 | 3 | Alright, so that's implementing a 100% safe doubly linked list in Rust. It 4 | relies on some unstable standard library features, and had massive 5 | implementation leak issues particularly surrounding acquiring internal 6 | references. 7 | 8 | It also was riddled with tons of "unnecessary" runtime checks for 9 | correctness between `Rc` and `RefCell`. I put unnecessary in quotes because 10 | they're actually necessary to guarantee the whole *actually being safe* thing. 11 | We encountered a few places where those checks actually *were* necessary. 12 | Doubly linked lists have a horribly tangled aliasing and ownership story! 13 | 14 | Still, it's a thing we can do. Especially if we don't care about exposing 15 | internal data structures to our consumers. 16 | 17 | From here on out, we're going to be focusing on other side of this coin: 18 | getting back all the control by making our implementation *unsafe*. 19 | 20 | ```rust 21 | use std::cell::{Ref, RefMut, RefCell}; 22 | use std::rc::Rc; 23 | 24 | pub struct List { 25 | head: Link, 26 | tail: Link, 27 | } 28 | 29 | type Link = Option>>>; 30 | 31 | struct Node { 32 | elem: T, 33 | next: Link, 34 | prev: Link, 35 | } 36 | 37 | pub struct IntoIter(List); 38 | 39 | impl Node { 40 | fn new(elem: T) -> Rc> { 41 | Rc::new(RefCell::new(Node { 42 | elem: elem, 43 | prev: None, 44 | next: None, 45 | })) 46 | } 47 | } 48 | 49 | impl List { 50 | pub fn new() -> Self { 51 | List { head: None, tail: None } 52 | } 53 | 54 | pub fn push_front(&mut self, elem: T) { 55 | let new_head = Node::new(elem); 56 | match self.head.take() { 57 | Some(old_head) => { 58 | old_head.borrow_mut().prev = Some(new_head.clone()); 59 | new_head.borrow_mut().next = Some(old_head); 60 | self.head = Some(new_head); 61 | } 62 | None => { 63 | self.tail = Some(new_head.clone()); 64 | self.head = Some(new_head); 65 | } 66 | } 67 | } 68 | 69 | pub fn push_back(&mut self, elem: T) { 70 | let new_tail = Node::new(elem); 71 | match self.tail.take() { 72 | Some(old_tail) => { 73 | old_tail.borrow_mut().next = Some(new_tail.clone()); 74 | new_tail.borrow_mut().prev = Some(old_tail); 75 | self.tail = Some(new_tail); 76 | } 77 | None => { 78 | self.head = Some(new_tail.clone()); 79 | self.tail = Some(new_tail); 80 | } 81 | } 82 | } 83 | 84 | pub fn pop_front(&mut self) -> Option { 85 | self.head.take().map(|old_head| { 86 | match old_head.borrow_mut().next.take() { 87 | Some(new_head) => { 88 | new_head.borrow_mut().prev.take(); 89 | self.head = Some(new_head); 90 | } 91 | None => { 92 | self.tail.take(); 93 | } 94 | } 95 | Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem 96 | }) 97 | } 98 | 99 | pub fn pop_back(&mut self) -> Option { 100 | self.tail.take().map(|old_tail| { 101 | match old_tail.borrow_mut().prev.take() { 102 | Some(new_tail) => { 103 | new_tail.borrow_mut().next.take(); 104 | self.tail = Some(new_tail); 105 | } 106 | None => { 107 | self.head.take(); 108 | } 109 | } 110 | Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem 111 | }) 112 | } 113 | 114 | pub fn peek_front(&self) -> Option> { 115 | self.head.as_ref().map(|node| { 116 | Ref::map(node.borrow(), |node| &node.elem) 117 | }) 118 | } 119 | 120 | pub fn peek_front_mut(&mut self) -> Option> { 121 | self.head.as_ref().map(|node| { 122 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 123 | }) 124 | } 125 | 126 | pub fn peek_back(&self) -> Option> { 127 | self.tail.as_ref().map(|node| { 128 | Ref::map(node.borrow(), |node| &node.elem) 129 | }) 130 | } 131 | 132 | pub fn peek_back_mut(&mut self) -> Option> { 133 | self.tail.as_ref().map(|node| { 134 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 135 | }) 136 | } 137 | 138 | pub fn into_iter(self) -> IntoIter { 139 | IntoIter(self) 140 | } 141 | } 142 | 143 | impl Drop for List { 144 | fn drop(&mut self) { 145 | while self.pop_front().is_some() {} 146 | } 147 | } 148 | 149 | impl Iterator for IntoIter { 150 | type Item = T; 151 | fn next(&mut self) -> Option { 152 | self.0.pop_front() 153 | } 154 | } 155 | 156 | impl DoubleEndedIterator for IntoIter { 157 | fn next_back(&mut self) -> Option { 158 | self.0.pop_back() 159 | } 160 | } 161 | 162 | #[cfg(test)] 163 | mod test { 164 | use super::List; 165 | 166 | #[test] 167 | fn basics() { 168 | let mut list = List::new(); 169 | 170 | // Check empty list behaves right 171 | assert_eq!(list.pop_front(), None); 172 | 173 | // Populate list 174 | list.push_front(1); 175 | list.push_front(2); 176 | list.push_front(3); 177 | 178 | // Check normal removal 179 | assert_eq!(list.pop_front(), Some(3)); 180 | assert_eq!(list.pop_front(), Some(2)); 181 | 182 | // Push some more just to make sure nothing's corrupted 183 | list.push_front(4); 184 | list.push_front(5); 185 | 186 | // Check normal removal 187 | assert_eq!(list.pop_front(), Some(5)); 188 | assert_eq!(list.pop_front(), Some(4)); 189 | 190 | // Check exhaustion 191 | assert_eq!(list.pop_front(), Some(1)); 192 | assert_eq!(list.pop_front(), None); 193 | 194 | // ---- back ----- 195 | 196 | // Check empty list behaves right 197 | assert_eq!(list.pop_back(), None); 198 | 199 | // Populate list 200 | list.push_back(1); 201 | list.push_back(2); 202 | list.push_back(3); 203 | 204 | // Check normal removal 205 | assert_eq!(list.pop_back(), Some(3)); 206 | assert_eq!(list.pop_back(), Some(2)); 207 | 208 | // Push some more just to make sure nothing's corrupted 209 | list.push_back(4); 210 | list.push_back(5); 211 | 212 | // Check normal removal 213 | assert_eq!(list.pop_back(), Some(5)); 214 | assert_eq!(list.pop_back(), Some(4)); 215 | 216 | // Check exhaustion 217 | assert_eq!(list.pop_back(), Some(1)); 218 | assert_eq!(list.pop_back(), None); 219 | } 220 | 221 | #[test] 222 | fn peek() { 223 | let mut list = List::new(); 224 | assert!(list.peek_front().is_none()); 225 | assert!(list.peek_back().is_none()); 226 | assert!(list.peek_front_mut().is_none()); 227 | assert!(list.peek_back_mut().is_none()); 228 | 229 | list.push_front(1); list.push_front(2); list.push_front(3); 230 | 231 | assert_eq!(&*list.peek_front().unwrap(), &3); 232 | assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); 233 | assert_eq!(&*list.peek_back().unwrap(), &1); 234 | assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); 235 | } 236 | 237 | #[test] 238 | fn into_iter() { 239 | let mut list = List::new(); 240 | list.push_front(1); list.push_front(2); list.push_front(3); 241 | 242 | let mut iter = list.into_iter(); 243 | assert_eq!(iter.next(), Some(3)); 244 | assert_eq!(iter.next_back(), Some(1)); 245 | assert_eq!(iter.next(), Some(2)); 246 | assert_eq!(iter.next_back(), None); 247 | assert_eq!(iter.next(), None); 248 | } 249 | } 250 | ``` 251 | -------------------------------------------------------------------------------- /fourth-iteration.md: -------------------------------------------------------------------------------- 1 | % Iteration 2 | 3 | Let's take a crack at iterating this bad-boy. 4 | 5 | ## IntoIter 6 | 7 | IntoIter, as always, is going to be the easiest. Just wrap the stack and 8 | call `pop`: 9 | 10 | ```rust 11 | pub struct IntoIter(List); 12 | 13 | impl List { 14 | pub fn into_iter(self) -> IntoIter { 15 | IntoIter(self) 16 | } 17 | } 18 | 19 | impl Iterator for IntoIter { 20 | type Item = T; 21 | fn next(&mut self) -> Option { 22 | self.0.pop_front() 23 | } 24 | } 25 | ``` 26 | 27 | But we have an interesting new development. Where previously there was only 28 | ever one "natural" iteration order for our lists, a Deque is inherently 29 | bi-directional. What's so special about front-to-back? What if someone wants 30 | to iterate in the other direction? 31 | 32 | Rust actually has an answer to this: `DoubleEndedIterator`. DoubleEndedIterator 33 | *inherits* from Iterator (meaning all DoubleEndedIterator are Iterators) and 34 | requires one new method: `next_back`. It has the exact same signature as 35 | `next`, but it's supposed to yield elements from the other end. The semantics 36 | of DoubleEndedIterator are super convenient for us: the iterator becomes a 37 | deque. You can consume elements from the front and back until the two ends 38 | converge, at which point the iterator is empty. 39 | 40 | Much like Iterator and `next`, it turns out that `next_back` isn't really 41 | something consumers of the DoubleEndedIterator really care about. Rather, the 42 | best part of this interface is that it exposes the `rev` method, which wraps 43 | up the iterator to make a new one that yields the elements in reverse order. 44 | The semantics of this are fairly straight-forward: calls to `next` on the 45 | reversed iterator are just calls to `next_back`. 46 | 47 | Anyway, because we're already a deque providing this API is pretty easy: 48 | 49 | ```rust 50 | impl DoubleEndedIterator for IntoIter { 51 | fn next_back(&mut self) -> Option { 52 | self.0.pop_back() 53 | } 54 | } 55 | ``` 56 | 57 | And let's test it out: 58 | 59 | ```rust 60 | #[test] 61 | fn into_iter() { 62 | let mut list = List::new(); 63 | list.push_front(1); list.push_front(2); list.push_front(3); 64 | 65 | let mut iter = list.into_iter(); 66 | assert_eq!(iter.next(), Some(3)); 67 | assert_eq!(iter.next_back(), Some(1)); 68 | assert_eq!(iter.next(), Some(2)); 69 | assert_eq!(iter.next_back(), None); 70 | assert_eq!(iter.next(), None); 71 | } 72 | ``` 73 | 74 | 75 | ```text 76 | cargo test 77 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 78 | Running target/debug/lists-5c71138492ad4b4a 79 | 80 | running 11 tests 81 | test fourth::test::basics ... ok 82 | test fourth::test::peek ... ok 83 | test fourth::test::into_iter ... ok 84 | test first::test::basics ... ok 85 | test second::test::basics ... ok 86 | test second::test::iter ... ok 87 | test second::test::iter_mut ... ok 88 | test third::test::iter ... ok 89 | test third::test::basics ... ok 90 | test second::test::into_iter ... ok 91 | test second::test::peek ... ok 92 | 93 | test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured 94 | 95 | Doc-tests lists 96 | 97 | running 0 tests 98 | 99 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 100 | ``` 101 | 102 | Nice. 103 | 104 | ## Iter 105 | 106 | Iter will be a bit less forgiving. We'll have to deal with those awful `Ref` 107 | things again! Because of Refs, we can't store `&Node`s like we did before. 108 | Instead, let's try to store `Ref`s: 109 | 110 | ```rust 111 | pub struct Iter<'a, T: 'a>(Option>>); 112 | 113 | impl List { 114 | pub fn iter(&self) -> Iter { 115 | Iter(self.head.as_ref().map(|head| head.borrow())) 116 | } 117 | } 118 | ``` 119 | 120 | ```text 121 | > cargo build 122 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 123 | ``` 124 | 125 | So far so good. Implementing `next` is going to be a bit hairy, but I think 126 | it's the same basic logic as the old stack IterMut but with extra RefCell 127 | madness: 128 | 129 | ```rust 130 | impl<'a, T> Iterator for Iter<'a, T> { 131 | type Item = Ref<'a, T>; 132 | fn next(&mut self) -> Option { 133 | self.0.take().map(|node_ref| { 134 | self.0 = node_ref.next.as_ref().map(|head| head.borrow()); 135 | Ref::map(node_ref, |node| &node.elem) 136 | }) 137 | } 138 | } 139 | ``` 140 | 141 | ```text 142 | cargo build 143 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 144 | src/fourth.rs:154:22: 154:30 error: `node_ref` does not live long enough 145 | src/fourth.rs:154 self.0 = node_ref.next.as_ref().map(|head| head.borrow()); 146 | ^~~~~~~~ 147 | note: in expansion of closure expansion 148 | src/fourth.rs:153:27: 156:10 note: expansion site 149 | src/fourth.rs:152:46: 157:6 note: reference must be valid for the lifetime 'a as defined on the block at 152:45... 150 | src/fourth.rs:152 fn next(&mut self) -> Option { 151 | src/fourth.rs:153 self.0.take().map(|node_ref| { 152 | src/fourth.rs:154 self.0 = node_ref.next.as_ref().map(|head| head.borrow()); 153 | src/fourth.rs:155 Ref::map(node_ref, |node| &node.elem) 154 | src/fourth.rs:156 }) 155 | src/fourth.rs:157 } 156 | src/fourth.rs:153:38: 156:10 note: ...but borrowed value is only valid for the scope of parameters for function at 153:37 157 | src/fourth.rs:153 self.0.take().map(|node_ref| { 158 | src/fourth.rs:154 self.0 = node_ref.next.as_ref().map(|head| head.borrow()); 159 | src/fourth.rs:155 Ref::map(node_ref, |node| &node.elem) 160 | src/fourth.rs:156 }) 161 | error: aborting due to previous error 162 | Could not compile `lists`. 163 | ``` 164 | 165 | Shoot. 166 | 167 | `node_ref` doesn't live long enough. Unlike normal references, Rust doesn't let 168 | us just split Refs up like that. The Ref we get out of `head.borrow()` is only 169 | allowed to live as long as `node_ref`, but we end up trashing that in our 170 | `Ref::map` call. 171 | 172 | I don't think there's anything we can do here. It's a dead end. Let's try 173 | getting out of the RefCells. What if we store an `&RefCell`? 174 | 175 | 176 | ```rust 177 | pub struct Iter<'a, T: 'a>(Option<&'a RefCell>>); 178 | 179 | impl List { 180 | pub fn iter(&self) -> Iter { 181 | Iter(self.head.as_ref().map(|head| &** head)) 182 | } 183 | } 184 | 185 | impl<'a, T> Iterator for Iter<'a, T> { 186 | type Item = Ref<'a, T>; 187 | fn next(&mut self) -> Option { 188 | self.0.take().map(|node| { 189 | self.0 = node.borrow().next.as_ref().map(|head| &**head); 190 | Ref::map(node.borrow(), |node| &node.elem) 191 | }) 192 | } 193 | } 194 | ``` 195 | 196 | ```text 197 | > cargo build 198 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 199 | src/fourth.rs:154:22: 154:35 error: borrowed value does not live long enough 200 | src/fourth.rs:154 self.0 = node.borrow().next.as_ref().map(|head| &**head); 201 | ^~~~~~~~~~~~~ 202 | note: in expansion of closure expansion 203 | src/fourth.rs:153:27: 156:10 note: expansion site 204 | src/fourth.rs:152:46: 157:6 note: reference must be valid for the lifetime 'a as defined on the block at 152:45... 205 | src/fourth.rs:152 fn next(&mut self) -> Option { 206 | src/fourth.rs:153 self.0.take().map(|node| { 207 | src/fourth.rs:154 self.0 = node.borrow().next.as_ref().map(|head| &**head); 208 | src/fourth.rs:155 Ref::map(node.borrow(), |node| &node.elem) 209 | src/fourth.rs:156 }) 210 | src/fourth.rs:157 } 211 | src/fourth.rs:154:13: 154:70 note: ...but borrowed value is only valid for the statement at 154:12 212 | src/fourth.rs:154 self.0 = node.borrow().next.as_ref().map(|head| &**head); 213 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 214 | note: in expansion of closure expansion 215 | src/fourth.rs:153:27: 156:10 note: expansion site 216 | src/fourth.rs:154:13: 154:70 help: consider using a `let` binding to increase its lifetime 217 | src/fourth.rs:154 self.0 = node.borrow().next.as_ref().map(|head| &**head); 218 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 219 | note: in expansion of closure expansion 220 | src/fourth.rs:153:27: 156:10 note: expansion site 221 | error: aborting due to previous error 222 | Could not compile `lists`. 223 | ``` 224 | 225 | Hmm... no this is the same problem again. The `borrow` makes a Ref that we can't 226 | keep alive long enough. 227 | 228 | Hang on, though. We've got `Rc`s. Who said we even needed to store references? 229 | Why can't we just Clone the whole Rc to get a nice owning handle into the middle 230 | of the list? 231 | 232 | ```rust 233 | pub struct Iter(Option>>); 234 | 235 | impl List { 236 | pub fn iter(&self) -> Iter { 237 | Iter(self.head.as_ref().map(|head| head.clone())) 238 | } 239 | } 240 | 241 | impl Iterator for Iter { 242 | type Item = 243 | ``` 244 | 245 | Uh... Wait what do we return now? `&T`? `Ref`? 246 | 247 | No, none of those work... our Iter doesn't have a lifetime anymore! Both `&T` 248 | and `Ref` require us to declare some lifetime up front before we get into 249 | `next`. But anything we manage to get out of our Rc would be borrowing the 250 | Iterator... brain... hurt... aaaaaahhhhhh 251 | 252 | Maybe we can... map... the Rc... to get an `Rc`? Is that a thing? Rc's docs 253 | don't seem to have anything like that. Actually someone made [a crate][own-ref] 254 | that lets you do that. 255 | 256 | But wait, even if we do *that* then we've got an even bigger problem: the 257 | dreaded spectre of iterator invalidation. Previously we've been totally immune 258 | to iterator invalidation, because the Iter borrowed the list, leaving it totally 259 | immutable. However now that our Iter is full of Rcs, it doesn't borrow the list 260 | at all! That means people can start calling `push` and `pop` on the list while 261 | they have an Iter! 262 | 263 | Oh lord, what will that do?! 264 | 265 | Well, pushing is actually fine. We've got a view into some sub-range of the 266 | list, and the list will just grow beyond our sights. No biggie. 267 | 268 | However `pop` is another story. If they're popping elements outside outside of 269 | our range, it should *still* be fine. We can't see those nodes so nothing will 270 | happen. However if they try to pop off the node we're pointing at... everything 271 | will blow up! In particular when they go to `unwrap` the result of the 272 | `try_unwrap`, it will actually fail, and the whole program will panic. 273 | 274 | That's actually pretty cool. We can get tons of interior owning pointers into 275 | the list and mutate it at the same time *and it will just work* until they 276 | try to remove the nodes that we're pointing at. And even then we don't get 277 | dangling pointers or anything, the program will deterministically panic! 278 | 279 | But having to deal with iterator invalidation on top of mapping Rcs just 280 | seems... lame. `Rc` has really truly finally failed us. Interestingly, 281 | we've experienced an inversion of the persistent stack case. Where the 282 | persistent stack struggled to ever reclaim ownership of the data but could get 283 | references all day every day, our list had no problem gaining ownership, but 284 | really struggled to loan our references. 285 | 286 | Although to be fair, most of our struggles revolved around wanting to hide the 287 | implementation details and have a decent API. We *could* do everything fine 288 | if we wanted to just pass around Nodes all over the place. 289 | 290 | Heck, we could make multiple concurrent IterMuts that were runtime checked to 291 | not be mutable accessing the same element! 292 | 293 | Really, this design is more appropriate for an internal data structure that 294 | never makes it out to consumers of the API. Interior mutability is great for 295 | writing safe *applications*. Not so much safe *libraries*. 296 | 297 | Anyway, that's me giving up on Iter and IterMut. We could do them, but *ugh*. 298 | 299 | [own-ref]: 300 | -------------------------------------------------------------------------------- /fourth-layout.md: -------------------------------------------------------------------------------- 1 | % Layout 2 | 3 | The key to our design is the `RefCell` type. The heart of 4 | RefCell is a pair of methods: 5 | 6 | ```rust 7 | fn borrow<'a>(&'a self) -> Ref<'a, T> 8 | fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> 9 | ``` 10 | 11 | The rules for `borrow` and `borrow_mut` are exactly those of `&` and `&mut`: 12 | you can call `borrow` as many times as you want, but `borrow_mut` requires 13 | exclusivity. 14 | 15 | Rather than enforcing this statically, RefCell enforces them at runtime. 16 | If you break the rules, RefCell will just panic and crash the program. 17 | Why does it return these Ref and RefMut things? Well, they basically behave 18 | like `Rc`s but for borrowing. They keep the RefCell borrowed until they go out 19 | of scope. We'll get to that later. 20 | 21 | Now with Rc and RefCell we can become... an incredibly verbose pervasively 22 | mutable garbage collected language that can't collect cycles! Y-yaaaaay... 23 | 24 | Alright, we want to be *doubly linked*. This means each node has a pointer to 25 | the previous and next node. Also, the list itself has a pointer to the 26 | first and last node. This gives us fast insertion and removal on *both* 27 | ends of the list. 28 | 29 | So we probably want something like: 30 | 31 | ```rust 32 | use std::rc::Rc; 33 | use std::cell::RefCell; 34 | 35 | pub struct List { 36 | head: Link, 37 | tail: Link, 38 | } 39 | 40 | type Link = Option>>>; 41 | 42 | struct Node { 43 | elem: T, 44 | next: Link, 45 | prev: Link, 46 | } 47 | ``` 48 | 49 | ```text 50 | > cargo build 51 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 52 | src/fourth.rs:5:5: 5:18 warning: struct field is never used: `head`, #[warn(dead_code)] on by default 53 | src/fourth.rs:5 head: Link, 54 | ^~~~~~~~~~~~~ 55 | src/fourth.rs:6:5: 6:18 warning: struct field is never used: `tail`, #[warn(dead_code)] on by default 56 | src/fourth.rs:6 tail: Link, 57 | ^~~~~~~~~~~~~ 58 | src/fourth.rs:11:1: 15:2 warning: struct is never used: `Node`, #[warn(dead_code)] on by default 59 | src/fourth.rs:11 struct Node { 60 | src/fourth.rs:12 elem: T, 61 | src/fourth.rs:13 next: Link, 62 | src/fourth.rs:14 prev: Link, 63 | src/fourth.rs:15 } 64 | src/fourth.rs:12:5: 12:12 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 65 | src/fourth.rs:12 elem: T, 66 | ^~~~~~~ 67 | src/fourth.rs:13:5: 13:18 warning: struct field is never used: `next`, #[warn(dead_code)] on by default 68 | src/fourth.rs:13 next: Link, 69 | ^~~~~~~~~~~~~ 70 | src/fourth.rs:14:5: 14:18 warning: struct field is never used: `prev`, #[warn(dead_code)] on by default 71 | src/fourth.rs:14 prev: Link, 72 | ^~~~~~~~~~~~~ 73 | ``` 74 | 75 | Hey, it built! Lots of dead code warnings, but it built! Let's try to use it. 76 | -------------------------------------------------------------------------------- /fourth-peek.md: -------------------------------------------------------------------------------- 1 | % Peeking 2 | 3 | Alright, we made it through `push` and `pop`. I'm not gonna lie, it got a 4 | bit emotional there. Compile-time correctness is a hell of a drug. 5 | 6 | Let's cool off by doing something simple: let's just implement `peek_front`. 7 | That was always really easy before. Gotta still be easy, right? 8 | 9 | Right? 10 | 11 | In fact, I think I can just copy-paste it! 12 | 13 | ```rust 14 | pub fn peek_front(&self) -> Option<&T> { 15 | self.head.as_ref().map(|node| { 16 | &node.elem 17 | }) 18 | } 19 | ``` 20 | 21 | Wait. Not this time. 22 | 23 | ```rust 24 | pub fn peek_front(&self) -> Option<&T> { 25 | self.head.as_ref().map(|node| { 26 | &node.borrow().elem 27 | }) 28 | } 29 | ``` 30 | 31 | HAH. 32 | 33 | ```text 34 | cargo build 35 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 36 | src/fourth.rs:64:14: 64:27 error: borrowed value does not live long enough 37 | src/fourth.rs:64 &node.borrow().elem 38 | ^~~~~~~~~~~~~ 39 | note: in expansion of closure expansion 40 | src/fourth.rs:63:32: 65:10 note: expansion site 41 | src/fourth.rs:62:44: 66:6 note: reference must be valid for the anonymous lifetime #1 defined on the block at 62:43... 42 | src/fourth.rs:62 pub fn peek_front(&self) -> Option<&T> { 43 | src/fourth.rs:63 self.head.as_ref().map(|node| { 44 | src/fourth.rs:64 &node.borrow().elem 45 | src/fourth.rs:65 }) 46 | src/fourth.rs:66 } 47 | src/fourth.rs:63:39: 65:10 note: ...but borrowed value is only valid for the block at 63:38 48 | src/fourth.rs:63 self.head.as_ref().map(|node| { 49 | src/fourth.rs:64 &node.borrow().elem 50 | src/fourth.rs:65 }) 51 | error: aborting due to previous error 52 | Could not compile `lists`. 53 | ``` 54 | 55 | Ok I'm just burning my computer. 56 | 57 | This is exactly the same logic as our singly linked stack. Why are things 58 | different. WHY. 59 | 60 | The answer is really the whole moral of this chapter: RefCells make everything 61 | sadness. Up until now, RefCells have just been a nuisance. Now they're going to 62 | become a nightmare. 63 | 64 | So what's going on? To understand that, we need to go back to the definition of 65 | `borrow`: 66 | 67 | ```rust 68 | fn borrow<'a>(&'a self) -> Ref<'a, T> 69 | fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> 70 | ``` 71 | 72 | In the layout section we said: 73 | 74 | > Rather than enforcing this statically, RefCell enforces them at runtime. 75 | > If you break the rules, RefCell will just panic and crash the program. 76 | > Why does it return these Ref and RefMut things? Well, they basically behave 77 | > like `Rc`s but for borrowing. They keep the RefCell borrowed until they go out 78 | > of scope. We'll get to that later. 79 | 80 | It's later. 81 | 82 | `Ref` and `RefMut` implement `Deref` and `DerefMut` respectively. So for most 83 | intents and purposes they behave *exactly* like `&T` and `&mut T`. However, 84 | because of how those traits work, the reference that's returned is connected 85 | to the lifetime of the Ref, and not actual RefCell. This means that the Ref 86 | has to be sitting around as long as we keep the reference around. 87 | 88 | This is in fact necessary for correctness. When a Ref gets dropped, it tells 89 | the RefCell that it's not borrowed anymore. So if *did* manage to hold onto our 90 | reference longer than the Ref existed, we could get a RefMut while a reference 91 | was kicking around and totally break Rust's type system in half. 92 | 93 | So where does that leave us? We only want to return a reference, but we need 94 | to keep this Ref thing around. But as soon as we return the reference from 95 | `peek`, the function is over and the `Ref` goes out of scope. 96 | 97 | 😖 98 | 99 | As far as I know, we're actually totally dead in the water here. You can't 100 | totally encapsulate the use of RefCells like that. 101 | 102 | But... what if we just give up on totally hiding our implementation details? 103 | What if we returns Refs? 104 | 105 | ```rust 106 | pub fn peek_front(&self) -> Option> { 107 | self.head.as_ref().map(|node| { 108 | node.borrow() 109 | }) 110 | } 111 | ``` 112 | 113 | ```text 114 | > cargo build 115 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 116 | src/fourth.rs:62:40: 62:46 error: use of undeclared type name `Ref` [E0412] 117 | src/fourth.rs:62 pub fn peek_front(&self) -> Option> { 118 | ^~~~~~ 119 | error: aborting due to previous error 120 | Could not compile `lists`. 121 | ``` 122 | 123 | Blurp. Gotta import some stuff. 124 | 125 | 126 | ```rust 127 | use std::cell::{Ref, RefMut, RefCell}; 128 | ``` 129 | 130 | ```text 131 | > cargo build 132 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 133 | src/fourth.rs:63:9: 65:11 error: mismatched types: 134 | expected `core::option::Option>`, 135 | found `core::option::Option>>` 136 | (expected type parameter, 137 | found struct `fourth::Node`) [E0308] 138 | src/fourth.rs:63 self.head.as_ref().map(|node| { 139 | src/fourth.rs:64 node.borrow() 140 | src/fourth.rs:65 }) 141 | src/fourth.rs:63:9: 65:11 help: run `rustc --explain E0308` to see a detailed explanation 142 | error: aborting due to previous error 143 | Could not compile `lists`. 144 | ``` 145 | 146 | Hmm... that's right. We have a `Ref>`, but we want a `Ref`. We could 147 | abandon all hope of encapsulation and just return that. We could also make 148 | things even more complicated and wrap `Ref>` in a new type to only 149 | expose access to an `&T`. 150 | 151 | Both of those options are *kinda* lame. 152 | 153 | Instead, we're going to go deeper down the nightly unstable features hole. This 154 | one is actually gratuitous since the newtype solution is actually fine. But 155 | we're already on nightly, and this list already has me deeply depressed. Let's 156 | have some *fun*. Our source of fun is *this beast*: 157 | 158 | ```rust 159 | map(orig: Ref<'b, T>, f: F) -> Ref<'b, U> 160 | where F: FnOnce(&T) -> &U, 161 | U: ?Sized 162 | ``` 163 | 164 | > Make a new Ref for a component of the borrowed data. 165 | 166 | Yes: just like you can map over an Option, you can map over a Ref. 167 | 168 | I'm sure someone somewhere is really excited because *monads* or whatever but 169 | I don't care about any of that. Also I don't think it's a proper monad since 170 | there's no None-like case. But I digress. 171 | 172 | It's cool and that's all that matters to me. *I need this*. 173 | 174 | ```rust 175 | pub fn peek_front(&self) -> Option> { 176 | self.head.as_ref().map(|node| { 177 | Ref::map(node.borrow(), |node| &node.elem) 178 | }) 179 | } 180 | ``` 181 | 182 | ```text 183 | > cargo build 184 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 185 | src/fourth.rs:64:13: 64:21 error: use of unstable library feature 'cell_extras': recently added 186 | src/fourth.rs:64 Ref::map(node.borrow(), |node| &node.elem) 187 | ^~~~~~~~ 188 | note: in expansion of closure expansion 189 | src/fourth.rs:63:32: 65:10 note: expansion site 190 | src/fourth.rs:64:13: 64:21 help: add #![feature(cell_extras)] to the crate attributes to enable 191 | src/fourth.rs:1:22: 1:28 warning: unused import, #[warn(unused_imports)] on by default 192 | src/fourth.rs:1 use std::cell::{Ref, RefMut, RefCell}; 193 | ^~~~~~ 194 | error: aborting due to previous error 195 | Could not compile `lists`. 196 | ``` 197 | 198 | *Yeah Yeah...* 199 | 200 | ```rust 201 | // in lib.rs 202 | #![feature(rc_unique, cell_extras)] 203 | ``` 204 | 205 | ```text 206 | > cargo build 207 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 208 | src/fourth.rs:1:22: 1:28 warning: unused import, #[warn(unused_imports)] on by default 209 | src/fourth.rs:1 use std::cell::{Ref, RefMut, RefCell}; 210 | ^~~~~~ 211 | ``` 212 | 213 | Awww yissss 214 | 215 | Let's make sure this is working by munging up the test from our stack. We need 216 | to do some munging to deal with the fact that Refs don't implement comparisons. 217 | 218 | ```rust 219 | #[test] 220 | fn peek() { 221 | let mut list = List::new(); 222 | assert!(list.peek_front().is_none()); 223 | list.push_front(1); list.push_front(2); list.push_front(3); 224 | 225 | assert_eq!(&*list.peek_front().unwrap(), &3); 226 | } 227 | ``` 228 | 229 | 230 | ``` 231 | > cargo test 232 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 233 | src/fourth.rs:1:22: 1:28 warning: unused import, #[warn(unused_imports)] on by default 234 | src/fourth.rs:1 use std::cell::{Ref, RefMut, RefCell}; 235 | ^~~~~~ 236 | Running target/debug/lists-5c71138492ad4b4a 237 | 238 | running 10 tests 239 | test first::test::basics ... ok 240 | test fourth::test::basics ... ok 241 | test second::test::basics ... ok 242 | test fourth::test::peek ... ok 243 | test second::test::iter_mut ... ok 244 | test second::test::into_iter ... ok 245 | test third::test::basics ... ok 246 | test second::test::peek ... ok 247 | test second::test::iter ... ok 248 | test third::test::iter ... ok 249 | 250 | test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured 251 | 252 | Doc-tests lists 253 | 254 | running 0 tests 255 | 256 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 257 | ``` 258 | 259 | Great! 260 | -------------------------------------------------------------------------------- /fourth-symmetry.md: -------------------------------------------------------------------------------- 1 | % Symmetric Junk 2 | 3 | Alright let's get all that combinatoric symmtery over with. 4 | 5 | All we have to do is some basic text replacement: 6 | 7 | ```text 8 | tail <-> head 9 | next <-> prev 10 | front -> back 11 | ``` 12 | 13 | Oh, also we need to add `_mut` variants for peeking. 14 | 15 | ```rust 16 | pub fn push_back(&mut self, elem: T) { 17 | let new_tail = Node::new(elem); 18 | match self.tail.take() { 19 | Some(old_tail) => { 20 | old_tail.borrow_mut().next = Some(new_tail.clone()); 21 | new_tail.borrow_mut().prev = Some(old_tail); 22 | self.tail = Some(new_tail); 23 | } 24 | None => { 25 | self.head = Some(new_tail.clone()); 26 | self.tail = Some(new_tail); 27 | } 28 | } 29 | } 30 | 31 | pub fn pop_back(&mut self) -> Option { 32 | self.tail.take().map(|old_tail| { 33 | match old_tail.borrow_mut().prev.take() { 34 | Some(new_tail) => { 35 | new_tail.borrow_mut().next.take(); 36 | self.tail = Some(new_tail); 37 | } 38 | None => { 39 | self.head.take(); 40 | } 41 | } 42 | Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem 43 | }) 44 | } 45 | 46 | pub fn peek_back(&self) -> Option> { 47 | self.tail.as_ref().map(|node| { 48 | Ref::map(node.borrow(), |node| &node.elem) 49 | }) 50 | } 51 | 52 | pub fn peek_back_mut(&mut self) -> Option> { 53 | self.tail.as_ref().map(|node| { 54 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 55 | }) 56 | } 57 | 58 | pub fn peek_front_mut(&mut self) -> Option> { 59 | self.head.as_ref().map(|node| { 60 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 61 | }) 62 | } 63 | ``` 64 | 65 | And massively flesh out our tests: 66 | 67 | 68 | ``` 69 | #[test] 70 | fn basics() { 71 | let mut list = List::new(); 72 | 73 | // Check empty list behaves right 74 | assert_eq!(list.pop_front(), None); 75 | 76 | // Populate list 77 | list.push_front(1); 78 | list.push_front(2); 79 | list.push_front(3); 80 | 81 | // Check normal removal 82 | assert_eq!(list.pop_front(), Some(3)); 83 | assert_eq!(list.pop_front(), Some(2)); 84 | 85 | // Push some more just to make sure nothing's corrupted 86 | list.push_front(4); 87 | list.push_front(5); 88 | 89 | // Check normal removal 90 | assert_eq!(list.pop_front(), Some(5)); 91 | assert_eq!(list.pop_front(), Some(4)); 92 | 93 | // Check exhaustion 94 | assert_eq!(list.pop_front(), Some(1)); 95 | assert_eq!(list.pop_front(), None); 96 | 97 | // ---- back ----- 98 | 99 | // Check empty list behaves right 100 | assert_eq!(list.pop_front(), None); 101 | 102 | // Populate list 103 | list.push_back(1); 104 | list.push_back(2); 105 | list.push_back(3); 106 | 107 | // Check normal removal 108 | assert_eq!(list.pop_back(), Some(3)); 109 | assert_eq!(list.pop_back(), Some(2)); 110 | 111 | // Push some more just to make sure nothing's corrupted 112 | list.push_back(4); 113 | list.push_back(5); 114 | 115 | // Check normal removal 116 | assert_eq!(list.pop_back(), Some(5)); 117 | assert_eq!(list.pop_back(), Some(4)); 118 | 119 | // Check exhaustion 120 | assert_eq!(list.pop_back(), Some(1)); 121 | assert_eq!(list.pop_back(), None); 122 | } 123 | 124 | #[test] 125 | fn peek() { 126 | let mut list = List::new(); 127 | assert!(list.peek_front().is_none()); 128 | assert!(list.peek_back().is_none()); 129 | assert!(list.peek_front_mut().is_none()); 130 | assert!(list.peek_back_mut().is_none()); 131 | 132 | list.push_front(1); list.push_front(2); list.push_front(3); 133 | 134 | assert_eq!(&*list.peek_front().unwrap(), &3); 135 | assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); 136 | assert_eq!(&*list.peek_back().unwrap(), &1); 137 | assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); 138 | } 139 | ``` 140 | 141 | Are there some cases we're not testing? Probably. The combinatoric space 142 | has really blown up here. Our code is at very least not *obviously wrong*. 143 | 144 | ```text 145 | > cargo test 146 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 147 | Running target/debug/lists-5c71138492ad4b4a 148 | 149 | running 10 tests 150 | test first::test::basics ... ok 151 | test fourth::test::basics ... ok 152 | test second::test::basics ... ok 153 | test fourth::test::peek ... ok 154 | test second::test::iter ... ok 155 | test third::test::iter ... ok 156 | test second::test::into_iter ... ok 157 | test second::test::iter_mut ... ok 158 | test second::test::peek ... ok 159 | test third::test::basics ... ok 160 | 161 | test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured 162 | 163 | Doc-tests lists 164 | 165 | running 0 tests 166 | 167 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 168 | ``` 169 | 170 | Nice. Copy-pasting is the best kind of programming. 171 | -------------------------------------------------------------------------------- /fourth.md: -------------------------------------------------------------------------------- 1 | % A Bad but Safe Doubly-Linked Deque 2 | 3 | Now that we've seen Rc and heard about interior mutability, this gives an 4 | interesting thought... maybe we *can* mutate through an Rc. And it *that's* 5 | the case, maybe we can implement a *doubly* linked list totally safely! 6 | 7 | In the process we'll become familiar with *interior mutability*, and probably 8 | learn the hard way that safe doesn't mean *correct*. Doubly linked lists are 9 | hard, and I always make a mistake somewhere. 10 | 11 | Let's add a new file called `fourth.rs`: 12 | 13 | ``` 14 | // in lib.rs 15 | 16 | pub mod first; 17 | pub mod second; 18 | pub mod third; 19 | pub mod fourth; 20 | ``` 21 | 22 | This will be another clean-room operation, though as usual we'll probably find 23 | some logic that applies verbatim again. 24 | 25 | -------------------------------------------------------------------------------- /indy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WeAthFoLD/rust-too-many-lists-zhcn/ab208b0492f8f56eccba3f3bee06ea2a1105a460/indy.gif -------------------------------------------------------------------------------- /infinity-double-single.md: -------------------------------------------------------------------------------- 1 | % The Double Singly-Linked List 2 | 3 | We struggled with doubly linked lists because they have tangled ownership 4 | semantics: no node strictly owns any other node. However we struggled 5 | with this because we brought in our preconceived notions of what a linked 6 | list *is*. Namely, we assumed that all the links go in the same direction. 7 | 8 | Instead, we can smash our list into two halves: one going to the left, 9 | and one going to the right: 10 | 11 | ``` 12 | // lib.rs 13 | // ... 14 | pub mod silly1; // NEW! 15 | ``` 16 | 17 | ```rust 18 | // silly1.rs 19 | use second::List as Stack; 20 | 21 | struct List { 22 | left: Stack, 23 | right: Stack, 24 | } 25 | ``` 26 | 27 | Now, rather than having a mere safe stack, we have a general purpose list. 28 | We can grow the list leftwards or rightwards by pushing onto either stack. 29 | We can also "walk" along the list by popping values off one end and onto the 30 | other. To avoid needless allocations, we're going to copy the source of 31 | our safe Stack to get access to its private details: 32 | 33 | ```rust 34 | pub struct Stack { 35 | head: Link, 36 | } 37 | 38 | type Link = Option>>; 39 | 40 | struct Node { 41 | elem: T, 42 | next: Link, 43 | } 44 | 45 | impl Stack { 46 | pub fn new() -> Self { 47 | Stack { head: None } 48 | } 49 | 50 | pub fn push(&mut self, elem: T) { 51 | let new_node = Box::new(Node { 52 | elem: elem, 53 | next: self.head.take(), 54 | }); 55 | 56 | self.head = Some(new_node); 57 | } 58 | 59 | pub fn pop(&mut self) -> Option { 60 | self.head.take().map(|node| { 61 | let node = *node; 62 | self.head = node.next; 63 | node.elem 64 | }) 65 | } 66 | 67 | pub fn peek(&self) -> Option<&T> { 68 | self.head.as_ref().map(|node| { 69 | &node.elem 70 | }) 71 | } 72 | 73 | pub fn peek_mut(&mut self) -> Option<&mut T> { 74 | self.head.as_mut().map(|node| { 75 | &mut node.elem 76 | }) 77 | } 78 | } 79 | 80 | impl Drop for Stack { 81 | fn drop(&mut self) { 82 | let mut cur_link = self.head.take(); 83 | while let Some(mut boxed_node) = cur_link { 84 | cur_link = boxed_node.next.take(); 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | And just rework `push` and `pop` a bit: 91 | 92 | ``` 93 | pub fn push(&mut self, elem: T) { 94 | let new_node = Box::new(Node { 95 | elem: elem, 96 | next: None, 97 | }); 98 | 99 | self.push_node(new_node); 100 | } 101 | 102 | fn push_node(&mut self, mut node: Box>) { 103 | node.next = self.head.take(); 104 | self.head = Some(node); 105 | } 106 | 107 | pub fn pop(&mut self) -> Option { 108 | self.pop_node().map(|node| { 109 | node.elem 110 | }) 111 | } 112 | 113 | fn pop_node(&mut self) -> Option>> { 114 | self.head.take().map(|mut node| { 115 | self.head = node.next.take(); 116 | node 117 | }) 118 | } 119 | ``` 120 | 121 | Now we can make our List: 122 | 123 | ```rust 124 | pub struct List { 125 | left: Stack, 126 | right: Stack, 127 | } 128 | 129 | impl List { 130 | fn new() -> Self { 131 | List { left: Stack::new(), right: Stack::new() } 132 | } 133 | } 134 | ``` 135 | 136 | And we can do the usual stuff: 137 | 138 | 139 | ```rust 140 | pub fn push_left(&mut self, elem: T) { self.left.push(elem) } 141 | pub fn push_right(&mut self, elem: T) { self.right.push(elem) } 142 | pub fn pop_left(&mut self) -> Option { self.left.pop() } 143 | pub fn pop_right(&mut self) -> Option { self.right.pop() } 144 | pub fn peek_left(&self) -> Option<&T> { self.left.peek() } 145 | pub fn peek_right(&self) -> Option<&T> { self.right.peek() } 146 | pub fn peek_left_mut(&mut self) -> Option<&mut T> { self.left.peek_mut() } 147 | pub fn peek_right_mut(&mut self) -> Option<&mut T> { self.right.peek_mut() } 148 | ``` 149 | 150 | But most interestingly, we can walk around! 151 | 152 | 153 | ```rust 154 | pub fn go_left(&mut self) -> bool { 155 | self.left.pop_node().map(|node| { 156 | self.right.push_node(node); 157 | }).is_some() 158 | } 159 | 160 | pub fn go_right(&mut self) -> bool { 161 | self.right.pop_node().map(|node| { 162 | self.left.push_node(node); 163 | }).is_some() 164 | } 165 | ``` 166 | 167 | We return booleans here as just a convenience to indicate whether we actually 168 | managed to move. Now let's test this baby out: 169 | 170 | ```rust 171 | #[cfg(test)] 172 | mod test { 173 | use super::List; 174 | 175 | #[test] 176 | fn walk_aboot() { 177 | let mut list = List::new(); // [_] 178 | 179 | list.push_left(0); // [0,_] 180 | list.push_right(1); // [0, _, 1] 181 | assert_eq!(list.peek_left(), Some(&0)); 182 | assert_eq!(list.peek_right(), Some(&1)); 183 | 184 | list.push_left(2); // [0, 2, _, 1] 185 | list.push_left(3); // [0, 2, 3, _, 1] 186 | list.push_right(4); // [0, 2, 3, _, 4, 1] 187 | 188 | while list.go_left() {} // [_, 0, 2, 3, 4, 1] 189 | 190 | assert_eq!(list.pop_left(), None); 191 | assert_eq!(list.pop_right(), Some(0)); // [_, 2, 3, 4, 1] 192 | assert_eq!(list.pop_right(), Some(2)); // [_, 3, 4, 1] 193 | 194 | list.push_left(5); // [5, _, 3, 4, 1] 195 | assert_eq!(list.pop_right(), Some(3)); // [5, _, 4, 1] 196 | assert_eq!(list.pop_left(), Some(5)); // [_, 4, 1] 197 | assert_eq!(list.pop_right(), Some(4)); // [_, 1] 198 | assert_eq!(list.pop_right(), Some(1)); // [_] 199 | 200 | assert_eq!(list.pop_right(), None); 201 | assert_eq!(list.pop_left(), None); 202 | 203 | } 204 | } 205 | ``` 206 | 207 | ```text 208 | > cargo test 209 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 210 | Running target/debug/lists-5c71138492ad4b4a 211 | 212 | running 16 tests 213 | test fifth::test::into_iter ... ok 214 | test fifth::test::basics ... ok 215 | test fifth::test::iter ... ok 216 | test fifth::test::iter_mut ... ok 217 | test fourth::test::into_iter ... ok 218 | test fourth::test::basics ... ok 219 | test fourth::test::peek ... ok 220 | test first::test::basics ... ok 221 | test second::test::into_iter ... ok 222 | test second::test::basics ... ok 223 | test second::test::iter ... ok 224 | test second::test::iter_mut ... ok 225 | test third::test::basics ... ok 226 | test third::test::iter ... ok 227 | test second::test::peek ... ok 228 | test silly1::test::walk_aboot ... ok 229 | 230 | test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured 231 | 232 | Doc-tests lists 233 | 234 | running 0 tests 235 | 236 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 237 | ``` 238 | 239 | This is an extreme example of a *finger* data structure, where we maintain 240 | some kind of finger into the structure, and as a consequence can support 241 | operations on locations in time proportional to the distance from the finger. 242 | 243 | We can make very fast changes to the list around our finger, but if we want 244 | to make changes far away from our finger we have to walk all the way over there. 245 | We can permanently walk over there by shifting the elements from one stack to 246 | the other, or we could just walk along the links with an `&mut` 247 | temporarily to do the changes. However the `&mut` can never go back up the 248 | list, while our finger can! 249 | 250 | -------------------------------------------------------------------------------- /infinity.md: -------------------------------------------------------------------------------- 1 | % A Bunch of Silly Lists 2 | 3 | Alright. That's it. We made all the lists. 4 | 5 | ahahahaha 6 | 7 | No 8 | 9 | There's always more lists. 10 | 11 | This chapter is a living document of the more ridiculous linked lists and how 12 | they interact with Rust. 13 | 14 | 1. [The Double Single](infinity-double-single.html) 15 | 2. TODO: BList? 16 | 3. TODO: SkipList? 17 | 4. TODO: std::channel? -- That's like another whole chapter. Or 3. 18 | -------------------------------------------------------------------------------- /second-final.md: -------------------------------------------------------------------------------- 1 | % Final Code 2 | 3 | Alright, that's it for the second list; here's the final code! 4 | 5 | ```rust 6 | # fn main() {} 7 | 8 | pub struct List { 9 | head: Link, 10 | } 11 | 12 | type Link = Option>>; 13 | 14 | struct Node { 15 | elem: T, 16 | next: Link, 17 | } 18 | 19 | pub struct IntoIter(List); 20 | 21 | pub struct Iter<'a, T:'a> { 22 | next: Option<&'a Node>, 23 | } 24 | 25 | pub struct IterMut<'a, T: 'a> { 26 | next: Option<&'a mut Node>, 27 | } 28 | 29 | 30 | 31 | impl List { 32 | pub fn new() -> Self { 33 | List { head: None } 34 | } 35 | 36 | pub fn push(&mut self, elem: T) { 37 | let new_node = Box::new(Node { 38 | elem: elem, 39 | next: self.head.take(), 40 | }); 41 | 42 | self.head = Some(new_node); 43 | } 44 | 45 | pub fn pop(&mut self) -> Option { 46 | self.head.take().map(|node| { 47 | let node = *node; 48 | self.head = node.next; 49 | node.elem 50 | }) 51 | } 52 | 53 | pub fn peek(&self) -> Option<&T> { 54 | self.head.as_ref().map(|node| { 55 | &node.elem 56 | }) 57 | } 58 | 59 | pub fn peek_mut(&mut self) -> Option<&mut T> { 60 | self.head.as_mut().map(|node| { 61 | &mut node.elem 62 | }) 63 | } 64 | 65 | pub fn into_iter(self) -> IntoIter { 66 | IntoIter(self) 67 | } 68 | 69 | pub fn iter(&self) -> Iter { 70 | Iter { next: self.head.as_ref().map(|node| &**node) } 71 | } 72 | 73 | pub fn iter_mut(&mut self) -> IterMut { 74 | IterMut { next: self.head.as_mut().map(|node| &mut **node) } 75 | } 76 | } 77 | 78 | impl Drop for List { 79 | fn drop(&mut self) { 80 | let mut cur_link = self.head.take(); 81 | while let Some(mut boxed_node) = cur_link { 82 | cur_link = boxed_node.next.take(); 83 | } 84 | } 85 | } 86 | 87 | impl Iterator for IntoIter { 88 | type Item = T; 89 | fn next(&mut self) -> Option { 90 | self.0.pop() 91 | } 92 | } 93 | 94 | impl<'a, T> Iterator for Iter<'a, T> { 95 | type Item = &'a T; 96 | 97 | fn next(&mut self) -> Option { 98 | self.next.map(|node| { 99 | self.next = node.next.as_ref().map(|node| &**node); 100 | &node.elem 101 | }) 102 | } 103 | } 104 | 105 | impl<'a, T> Iterator for IterMut<'a, T> { 106 | type Item = &'a mut T; 107 | 108 | fn next(&mut self) -> Option { 109 | self.next.take().map(|node| { 110 | self.next = node.next.as_mut().map(|node| &mut **node); 111 | &mut node.elem 112 | }) 113 | } 114 | } 115 | 116 | 117 | #[cfg(test)] 118 | mod test { 119 | use super::List; 120 | 121 | #[test] 122 | fn basics() { 123 | let mut list = List::new(); 124 | 125 | // Check empty list behaves right 126 | assert_eq!(list.pop(), None); 127 | 128 | // Populate list 129 | list.push(1); 130 | list.push(2); 131 | list.push(3); 132 | 133 | // Check normal removal 134 | assert_eq!(list.pop(), Some(3)); 135 | assert_eq!(list.pop(), Some(2)); 136 | 137 | // Push some more just to make sure nothing's corrupted 138 | list.push(4); 139 | list.push(5); 140 | 141 | // Check normal removal 142 | assert_eq!(list.pop(), Some(5)); 143 | assert_eq!(list.pop(), Some(4)); 144 | 145 | // Check exhaustion 146 | assert_eq!(list.pop(), Some(1)); 147 | assert_eq!(list.pop(), None); 148 | } 149 | 150 | #[test] 151 | fn peek() { 152 | let mut list = List::new(); 153 | assert_eq!(list.peek(), None); 154 | assert_eq!(list.peek_mut(), None); 155 | list.push(1); list.push(2); list.push(3); 156 | 157 | assert_eq!(list.peek(), Some(&3)); 158 | assert_eq!(list.peek_mut(), Some(&mut 3)); 159 | } 160 | 161 | #[test] 162 | fn into_iter() { 163 | let mut list = List::new(); 164 | list.push(1); list.push(2); list.push(3); 165 | 166 | let mut iter = list.into_iter(); 167 | assert_eq!(iter.next(), Some(3)); 168 | assert_eq!(iter.next(), Some(2)); 169 | assert_eq!(iter.next(), Some(1)); 170 | } 171 | 172 | #[test] 173 | fn iter() { 174 | let mut list = List::new(); 175 | list.push(1); list.push(2); list.push(3); 176 | 177 | let mut iter = list.iter(); 178 | assert_eq!(iter.next(), Some(&3)); 179 | assert_eq!(iter.next(), Some(&2)); 180 | assert_eq!(iter.next(), Some(&1)); 181 | } 182 | 183 | #[test] 184 | fn iter_mut() { 185 | let mut list = List::new(); 186 | list.push(1); list.push(2); list.push(3); 187 | 188 | let mut iter = list.iter_mut(); 189 | assert_eq!(iter.next(), Some(&mut 3)); 190 | assert_eq!(iter.next(), Some(&mut 2)); 191 | assert_eq!(iter.next(), Some(&mut 1)); 192 | } 193 | } 194 | 195 | ``` 196 | 197 | Getting beefier! 198 | -------------------------------------------------------------------------------- /second-generic.md: -------------------------------------------------------------------------------- 1 | % Making it all Generic 2 | 3 | We've already touched a bit on generics with Option and Box. However so 4 | far we've managed to avoid declaring any new type that is actually generic 5 | over arbitrary elements. 6 | 7 | It turns out that's actually really easy. Let's make all of our types generic 8 | right now: 9 | 10 | ```rust 11 | pub struct List { 12 | head: Link, 13 | } 14 | 15 | type Link = Option>>; 16 | 17 | struct Node { 18 | elem: T, 19 | next: Link, 20 | } 21 | ``` 22 | 23 | You just make everything a little more pointy, and suddenly your code is 24 | generic. Of course, we can't *just* do this, or else the compiler's going 25 | to be Super Mad. 26 | 27 | 28 | ```text 29 | > cargo test 30 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 31 | src/second.rs:12:6: 12:10 error: wrong number of type arguments: expected 1, found 0 [E0243] 32 | src/second.rs:12 impl List { 33 | ^~~~ 34 | src/second.rs:12:6: 12:10 help: run `rustc --explain E0243` to see a detailed explanation 35 | src/second.rs:35:15: 35:19 error: wrong number of type arguments: expected 1, found 0 [E0243] 36 | src/second.rs:35 impl Drop for List { 37 | ^~~~ 38 | src/second.rs:35:15: 35:19 help: run `rustc --explain E0243` to see a detailed explanation 39 | error: aborting due to 2 previous errors 40 | ``` 41 | 42 | The compiler is even telling us about some fancy error code, but honestly the 43 | problem is pretty clear, we're talking about this `List` thing but that's not 44 | real anymore. Like Option and Box, we now always have to talk about 45 | `List`. 46 | 47 | But what's the Something we use in all these impls? Just like List, we want our 48 | implementations to work with *all* the T's. So, just like List, let's make our 49 | `impl`s pointy: 50 | 51 | 52 | ```rust 53 | impl List { 54 | pub fn new() -> Self { 55 | List { head: None } 56 | } 57 | 58 | pub fn push(&mut self, elem: T) { 59 | let new_node = Box::new(Node { 60 | elem: elem, 61 | next: self.head.take(), 62 | }); 63 | 64 | self.head = Some(new_node); 65 | } 66 | 67 | pub fn pop(&mut self) -> Option { 68 | self.head.take().map(|node| { 69 | let node = *node; 70 | self.head = node.next; 71 | node.elem 72 | }) 73 | } 74 | } 75 | 76 | impl Drop for List { 77 | fn drop(&mut self) { 78 | let mut cur_link = self.head.take(); 79 | while let Some(mut boxed_node) = cur_link { 80 | cur_link = boxed_node.next.take(); 81 | } 82 | } 83 | } 84 | ``` 85 | 86 | ...and that's it! 87 | 88 | 89 | ``` 90 | > cargo test 91 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 92 | Running target/debug/lists-5c71138492ad4b4a 93 | 94 | running 2 tests 95 | test first::test::basics ... ok 96 | test second::test::basics ... ok 97 | 98 | test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured 99 | 100 | Doc-tests lists 101 | 102 | running 0 tests 103 | 104 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 105 | ``` 106 | 107 | All of our code is now completely generic over arbitrary values of T. Dang, 108 | Rust is *easy*. I'd like to make a particular shout-out to `new` which didn't 109 | even change: 110 | 111 | ```rust 112 | pub fn new() -> Self { 113 | List { head: None } 114 | } 115 | ``` 116 | 117 | Bask in the Glory that is Self, guardian of refactoring and copy-pasta coding. 118 | Also of interest, we don't write `List` when we construct an instance of 119 | list. That part's inferred for us based on the fact that we're returning it 120 | from a function that expects a `List`. 121 | 122 | Alright, let's move on to totally new *behaviour*! 123 | -------------------------------------------------------------------------------- /second-into-iter.md: -------------------------------------------------------------------------------- 1 | % IntoIter 2 | 3 | Collections are iterated in Rust using the *Iterator* trait. It's a bit more 4 | complicated than `Drop`: 5 | 6 | ```rust 7 | pub trait Iterator { 8 | type Item; 9 | fn next(&mut self) -> Option; 10 | } 11 | ``` 12 | 13 | The new kid on the block here is `type Item`. This is declaring that every 14 | implementation of Iterator has an *associated type* called Item. In this case, 15 | this is the type of this that it can spit out when you call `next`. 16 | 17 | The reason Iterator yields `Option` is because the interface 18 | coallesces the `has_next` and `get_next` concepts. When you have_next, you yield 19 | Some(value), and when you don't you yield None. This makes the 20 | API generally more ergonomic and safe to use and implement, while avoiding 21 | redundant checks and logic between `has_next` and `get_next`. Nice! 22 | 23 | Sadly, Rust has nothing like a `yield` statement, so we're going to have to 24 | implement the logic ourselves. Also, there's actually 3 different kinds of 25 | iterator each collection should endeavour to implement: 26 | 27 | * IntoIter - `T` 28 | * IterMut - `&mut T` 29 | * Iter - `&T` 30 | 31 | We actually already have all the tools to implement 32 | IntoIter using List's interface: just call `pop` over and over. As such, we'll 33 | just implement IntoIter as a newtype wrapper around List: 34 | 35 | 36 | ```rust 37 | // Tuple structs are an alternative form of struct, 38 | // useful for trivial wrappers around other types. 39 | pub struct IntoIter(List); 40 | 41 | impl List { 42 | pub fn into_iter(self) -> IntoIter { 43 | IntoIter(self) 44 | } 45 | } 46 | 47 | impl Iterator for IntoIter { 48 | type Item = T; 49 | fn next(&mut self) -> Option { 50 | // access fields of a tuple struct numerically 51 | self.0.pop() 52 | } 53 | } 54 | ``` 55 | 56 | And let's write a test: 57 | 58 | ```rust 59 | #[test] 60 | fn into_iter() { 61 | let mut list = List::new(); 62 | list.push(1); list.push(2); list.push(3); 63 | 64 | let mut iter = list.into_iter(); 65 | assert_eq!(iter.next(), Some(3)); 66 | assert_eq!(iter.next(), Some(2)); 67 | assert_eq!(iter.next(), Some(1)); 68 | } 69 | ``` 70 | 71 | ```text 72 | > cargo test 73 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 74 | Running target/debug/lists-5c71138492ad4b4a 75 | 76 | running 4 tests 77 | test first::test::basics ... ok 78 | test second::test::basics ... ok 79 | test second::test::into_iter ... ok 80 | test second::test::peek ... ok 81 | 82 | test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured 83 | 84 | Doc-tests lists 85 | 86 | running 0 tests 87 | 88 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 89 | ``` 90 | 91 | Nice! 92 | 93 | -------------------------------------------------------------------------------- /second-iter-mut.md: -------------------------------------------------------------------------------- 1 | % IterMut 2 | 3 | I'm gonna be honest, IterMut is crazy. Which in itself seems like a crazy 4 | thing to say; surely it's identical to Iter! 5 | 6 | Semantically, yes. However the nature of shared and mutable references means 7 | that Iter is "trivial" while IterMut is Legit Wizard Magic. 8 | 9 | The key insight comes from our implementation of Iterator for Iter: 10 | 11 | ```rust 12 | impl<'a, T> Iterator for Iter<'a, T> { 13 | type Item = &'a T; 14 | 15 | fn next(&mut self) -> Option { /* stuff */ } 16 | } 17 | ``` 18 | 19 | Which can be desugarred to: 20 | 21 | ```rust 22 | impl<'a, T> Iterator for Iter<'a, T> { 23 | type Item = &'a T; 24 | 25 | fn next<'b>(&'b mut self) -> Option<&'a T> { /* stuff */ } 26 | } 27 | ``` 28 | 29 | The signature of `next` establishes *no* constraint between the lifetime 30 | of the input and the output! Why do we care? It means we can call `next` 31 | over and over unconditionally! 32 | 33 | 34 | ```rust 35 | let mut list = List::new(); 36 | list.push(1); list.push(2); list.push(3); 37 | 38 | let mut iter = list.iter(); 39 | let x = iter.next().unwrap(); 40 | let y = iter.next().unwrap(); 41 | let z = iter.next().unwrap(); 42 | ``` 43 | 44 | Cool! 45 | 46 | This is *definitely fine* for shared references because the whole point is that 47 | you can have tons of them at once. However mutable references *can't* coexist. 48 | The whole point is that they're exclusive. 49 | 50 | The end result is that it's notably harder to write an IterMut using safe 51 | code (and we haven't gotten into what that even means yet...). Surprisingly, 52 | IterMut can actually be implemented for many structures completely safely! 53 | Borrow checking magic! 54 | 55 | We'll start by just taking the Iter code and changing everything to be mutable: 56 | 57 | ```rust 58 | pub struct IterMut<'a, T: 'a> { 59 | next: Option<&'a mut Node>, 60 | } 61 | 62 | impl List { 63 | pub fn iter_mut(&self) -> IterMut { 64 | IterMut { next: self.head.as_mut().map(|node| &mut **node) } 65 | } 66 | } 67 | 68 | impl<'a, T> Iterator for IterMut<'a, T> { 69 | type Item = &'a mut T; 70 | 71 | fn next(&mut self) -> Option { 72 | self.next.map(|node| { 73 | self.next = node.next.as_mut().map(|node| &mut **node); 74 | &mut node.elem 75 | }) 76 | } 77 | } 78 | ``` 79 | 80 | ```text 81 | > cargo build 82 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 83 | src/second.rs:96:25: 96:34 error: cannot borrow immutable field `self.head` as mutable 84 | src/second.rs:96 IterMut { next: self.head.as_mut().map(|node| &mut **node) } 85 | ^~~~~~~~~ 86 | src/second.rs:104:9: 104:13 error: cannot move out of borrowed content 87 | src/second.rs:104 self.next.map(|node| { 88 | ^~~~ 89 | error: aborting due to previous error 90 | ``` 91 | 92 | Oops! I actually accidentally made an error when writing the 93 | `iter` impl, but Copy saved the day. `&` is Copy, as we saw before. But 94 | that also means `Option<&>` is *also* Copy. So when we did `self.next.map` it 95 | was fine because the Option was just copied. Now we can't do that, because 96 | `&mut` isn't Copy (if you copied an &mut, you'd have two &mut's to the same 97 | location in memory, which is verboten. Instead, we should properly `take` 98 | the Option to get it. 99 | 100 | 101 | ```rust 102 | fn next(&mut self) -> Option { 103 | self.next.take().map(|node| { 104 | self.next = node.next.as_mut().map(|node| &mut **node); 105 | &mut node.elem 106 | }) 107 | } 108 | ``` 109 | 110 | ```text 111 | > cargo build 112 | src/second.rs:65:25: 65:34 error: cannot borrow immutable field `self.head` as mutable 113 | src/second.rs:65 IterMut { next: self.head.as_mut().map(|node| &mut **node) } 114 | ^~~~~~~~~ 115 | error: aborting due to previous error 116 | ``` 117 | 118 | Uh... what? Looks we messed up mutability somewhere in `iter_mut`: 119 | 120 | ```rust 121 | pub fn iter_mut(&self) -> IterMut { 122 | IterMut { next: self.head.as_mut().map(|node| &mut **node) } 123 | } 124 | ``` 125 | 126 | Classic copy-paste error. `self` is a shared reference! We can't 127 | get mutable references out of that! 128 | 129 | ```rust 130 | pub fn iter_mut(&mut self) -> IterMut { 131 | IterMut { next: self.head.as_mut().map(|node| &mut **node) } 132 | } 133 | ``` 134 | 135 | ```text 136 | > cargo build 137 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 138 | ``` 139 | 140 | Uh... wow. Holy shit! IterMut Just Works! 141 | 142 | Let's test this: 143 | 144 | 145 | ```rust 146 | #[test] 147 | fn iter_mut() { 148 | let mut list = List::new(); 149 | list.push(1); list.push(2); list.push(3); 150 | 151 | let mut iter = list.iter_mut(); 152 | assert_eq!(iter.next(), Some(&mut 3)); 153 | assert_eq!(iter.next(), Some(&mut 2)); 154 | assert_eq!(iter.next(), Some(&mut 1)); 155 | } 156 | ``` 157 | 158 | ```text 159 | > cargo test 160 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 161 | Running target/debug/lists-5c71138492ad4b4a 162 | 163 | running 6 tests 164 | test first::test::basics ... ok 165 | test second::test::basics ... ok 166 | test second::test::iter_mut ... ok 167 | test second::test::into_iter ... ok 168 | test second::test::iter ... ok 169 | test second::test::peek ... ok 170 | 171 | test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured 172 | 173 | Doc-tests lists 174 | 175 | running 0 tests 176 | 177 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 178 | ``` 179 | 180 | Yep. It works. 181 | 182 | Holy shit. 183 | 184 | What. 185 | 186 | Ok I mean it actually *is* supposed to work, but there's usually something 187 | stupid that gets in the way! Let's be clear here: 188 | 189 | We have just implemented a piece of code that takes a singly-linked list, and 190 | returns a mutable reference to every single element in the list at most once. 191 | And it's statically verified to do that. And it's totally safe. And we didn't 192 | have to do anything crazy. 193 | 194 | That's kind of a big deal, if you ask me. There are a couple reasons why 195 | this works: 196 | 197 | * We `take` the `Option<&mut>` so we have exclusive access to the mutable 198 | reference. No need to worry about someone looking at it again. 199 | * Rust understands that it's ok to shard a mutable reference into the subfields 200 | of the pointed-to struct, because there's no way to "go back up", and they're 201 | definitely disjoint. 202 | 203 | It turns out that you can apply this basic logic to get a safe IterMut for an 204 | array or a tree as well! You can even make the iterator DoubleEnded, so that 205 | you can consume the iterator from the front *and* the back at once! Wild! 206 | 207 | -------------------------------------------------------------------------------- /second-iter.md: -------------------------------------------------------------------------------- 1 | % Iter 2 | 3 | Alright, let's try to implement Iter. This time we won't be able to rely on 4 | List giving us all the features we want. We'll need to roll our own. The 5 | basic logic we want is to hold a pointer to the current node we want to yield 6 | next. Because that node may not exist (the list is empty or we're otherwise 7 | done iterating), we want that reference to be an Option. When we yield an 8 | element, we want to proceed to the current node's `next` node. 9 | 10 | Alright, let's try that: 11 | 12 | ```rust 13 | pub struct Iter { 14 | next: Option<&Node>, 15 | } 16 | 17 | impl List { 18 | pub fn iter(&self) -> Iter { 19 | Iter { next: self.head.map(|node| &*node) } 20 | } 21 | } 22 | 23 | impl Iterator for Iter { 24 | type Item = &T; 25 | fn next(&mut self) -> Option { 26 | self.next.map(|node| { 27 | self.next = node.next.map(|node| &*node); 28 | &node.elem 29 | }) 30 | } 31 | } 32 | ``` 33 | 34 | ```text 35 | > cargo build 36 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 37 | src/second.rs:62:18: 62:26 error: missing lifetime specifier [E0106] 38 | src/second.rs:62 next: Option<&Node>, 39 | ^~~~~~~~ 40 | src/second.rs:62:18: 62:26 help: run `rustc --explain E0106` to see a detailed explanation 41 | src/second.rs:72:17: 72:19 error: missing lifetime specifier [E0106] 42 | src/second.rs:72 type Item = &T; 43 | ^~ 44 | src/second.rs:72:17: 72:19 help: run `rustc --explain E0106` to see a detailed explanation 45 | error: aborting due to 2 previous errors 46 | ``` 47 | 48 | Oh god. Lifetimes. I've heard of these things. I hear they're a nightmare. Let's 49 | try that `--explain`: 50 | 51 | ``` 52 | > rustc --explain E0106 53 | This error indicates that a lifetime is missing from a type. If it is an error 54 | inside a function signature, the problem may be with failing to adhere to the 55 | lifetime elision rules (see below). 56 | 57 | Here are some simple examples of where you'll run into this error: 58 | 59 | struct Foo { x: &bool } // error 60 | struct Foo<'a> { x: &'a bool } // correct 61 | 62 | enum Bar { A(u8), B(&bool), } // error 63 | enum Bar<'a> { A(u8), B(&'a bool), } // correct 64 | 65 | type MyStr = &str; // error 66 | type MyStr<'a> = &'a str; //correct 67 | ... 68 | 69 | ``` 70 | 71 | That uh... that didn't really clarify much. But it looks like we should add 72 | those `'a` things to our struct? Let's try that. 73 | 74 | ``` 75 | pub struct Iter<'a, T> { 76 | next: Option<&'a Node>, 77 | } 78 | ``` 79 | 80 | ```text 81 | > cargo build 82 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 83 | src/second.rs:71:22: 71:29 error: wrong number of lifetime parameters: expected 1, found 0 [E0107] 84 | src/second.rs:71 impl Iterator for Iter { 85 | ^~~~~~~ 86 | src/second.rs:71:22: 71:29 help: run `rustc --explain E0107` to see a detailed explanation 87 | src/second.rs:72:17: 72:19 error: missing lifetime specifier [E0106] 88 | src/second.rs:72 type Item = &T; 89 | ^~ 90 | src/second.rs:72:17: 72:19 help: run `rustc --explain E0106` to see a detailed explanation 91 | error: aborting due to 2 previous errors 92 | 93 | ``` 94 | 95 | Alright I'm starting to see a pattern here... let's just go whole-hog here: 96 | 97 | ``` 98 | pub struct Iter<'a, T> { 99 | next: Option<&'a Node>, 100 | } 101 | 102 | impl<'a, T> List { 103 | pub fn iter(&'a self) -> Iter<'a, T> { 104 | Iter { next: self.head.map(|node| &'a *node) } 105 | } 106 | } 107 | 108 | impl<'a, T> Iterator for Iter<'a, T> { 109 | type Item = &'a T; 110 | fn next(&'a mut self) -> Option { 111 | self.next.map(|node| { 112 | self.next = node.next.map(|node| &'a *node); 113 | &'a node.elem 114 | }) 115 | } 116 | } 117 | ``` 118 | 119 | ```text 120 | > cargo build 121 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 122 | src/second.rs:67:34: 67:35 error: expected `:`, found `*` 123 | src/second.rs:67 self.head.map(|node| &'a **node) 124 | ^ 125 | Could not compile `lists`. 126 | ``` 127 | 128 | 129 | 130 | Oh god. We broke Rust. 131 | 132 | Maybe we should actually figure out what the heck this `'a` lifetime stuff 133 | even means. 134 | 135 | Lifetimes can scare off a lot of people because 136 | they're a change to something we've known and loved since the dawn of 137 | programming. We've actually managed to dodge lifetimes so far, even though 138 | they've been tangled throughout our programs this whole time. 139 | 140 | Lifetimes are unecessary in garbage collected languages because the garbage 141 | collector ensures that everything magically lives as long as it needs to. Most 142 | data in Rust is *manually* managed, so that data needs another solution. C and 143 | C++ give us a clear example what happens if you just let people take pointers 144 | to random data on the stack: pervasive unmangeable unsafety. This can be 145 | roughly seperated into two classes of error: 146 | 147 | * Holding a pointer to something that went out of scope 148 | * Holding a pointer to something that got mutated away 149 | 150 | Lifetimes solve both of these problems, and 99% of the time, they do this in 151 | a totally transparent way. 152 | 153 | So what's a lifetime? 154 | 155 | Quite simply, a lifetime is the name of a scope somewhere in a program. 156 | That's it. When a reference is tagged with a lifetime, we're saying that it 157 | has to be valid for that *entire* scope. Different things place requirements on 158 | how long a reference must and can be valid for. The entire lifetime system is in 159 | turn just a constraint-solving system that tries to minimize the scope of every 160 | reference. If it sucessfully finds a set of lifetimes that satisfies all the 161 | constraints, your program compiles! Otherwise you get an error back saying that 162 | something didn't live long enough. 163 | 164 | Within a function body you generally can't talk about lifetimes, and wouldn't 165 | want to *anyway*. The compiler has full information and can infer all the 166 | contraints and find the minimum lifetimes. However at the type and API-level, 167 | the compiler *doesn't* have all the information. It requires you to tell it 168 | about the relationship between different lifetimes so it can figure out what 169 | you're doing. 170 | 171 | In principle, those lifetimes *could* also be left out, but 172 | then checking all the borrows would be a huge whole-program analysis that would 173 | produce mind-bogglingly non-local errors. Rust's system means all borrow 174 | checking can be done in each function body independently, and all your errors 175 | should be fairly local (or your types have incorrect signatures). 176 | 177 | But we've written references in function signatures before, and it was fine! 178 | That's because there are certain cases that are so common that Rust will 179 | automatically pick the lifetimes for you. This is *lifetime elision*. 180 | 181 | In particular: 182 | 183 | ```rust,ignore 184 | // Only one reference in input, so the output must be derived from that input 185 | fn foo(&A) -> &B; // sugar for: 186 | fn foo<'a>(&'a A) -> &'a B; 187 | 188 | // Many inputs, assume they're all independent 189 | fn foo(&A, &B, &C); // sugar for: 190 | fn foo<'a, 'b, 'c>(&'a, &'b, &'c); 191 | 192 | // Methods, assume all output lifetimes are derived from `self` 193 | fn foo(&self, &B, &C) -> &D; // sugar for: 194 | fn foo<'a, 'b, 'c>(&'a self, &'b B, &'c C) -> &'a D; 195 | ``` 196 | 197 | So what does `fn foo<'a>(&'a A) -> &'a B` *mean*? In practical terms, all it 198 | means is that the input must live at least as long as the output. So if you keep 199 | the output around for a long time, this will *drag* the scope that the `&A` must 200 | be valid for to be larger and larger. 201 | 202 | With this system set up, Rust can ensure nothing is used after free, and nothing 203 | is mutated while outstanding references exist. It just makes sure the 204 | constraints all work out! 205 | 206 | Alright. So. Iter. 207 | 208 | Let's roll back to the no lifetimes state: 209 | 210 | ```rust 211 | pub struct Iter { 212 | next: Option<&Node>, 213 | } 214 | 215 | impl List { 216 | pub fn iter(&self) -> Iter { 217 | Iter { next: self.head.map(|node| &*node) } 218 | } 219 | } 220 | 221 | impl Iterator for Iter { 222 | type Item = &T; 223 | fn next(&mut self) -> Option { 224 | self.next.map(|node| { 225 | self.next = node.next.map(|node| &*node); 226 | &node.elem 227 | }) 228 | } 229 | } 230 | ``` 231 | 232 | We need to add lifetimes only in function and type signatures: 233 | 234 | ``` 235 | // Iter is generic over *some* lifetime, it doesn't care 236 | pub struct Iter<'a, T> { 237 | next: Option<&'a Node>, 238 | } 239 | 240 | // No lifetime here, List doesn't have any associated lifetimes 241 | impl List { 242 | // We declare a fresh lifetime here for the *exact* borrow that 243 | // creates the iter. Now &self needs to be valid as long as the 244 | // Iter is around. 245 | pub fn iter<'a>(&'a self) -> Iter<'a, T> { 246 | Iter { next: self.head.map(|node| &*node) } 247 | } 248 | } 249 | 250 | // *Do* have a lifetime here, because Iter does have an associated lifetime 251 | impl<'a, T> Iterator for Iter<'a, T> { 252 | // Need it here too, this is a type declaration 253 | type Item = &'a T; 254 | 255 | // None of this needs to change, handled by the above. 256 | // Self continues to be incredibly hype and amazing 257 | fn next(&mut self) -> Option { 258 | self.next.map(|node| { 259 | self.next = node.next.map(|node| &**node); 260 | &node.elem 261 | }) 262 | } 263 | } 264 | ``` 265 | 266 | Alright, I think we got it this time y'all. 267 | 268 | ```text 269 | > cargo build 270 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 271 | src/second.rs:62:1: 64:2 error: the parameter type `T` may not live long enough [E0309] 272 | src/second.rs:62 pub struct Iter<'a, T> { 273 | src/second.rs:63 next: Option<&'a Node>, 274 | src/second.rs:64 } 275 | src/second.rs:62:1: 64:2 help: run `rustc --explain E0309` to see a detailed explanation 276 | src/second.rs:62:1: 64:2 help: consider adding an explicit lifetime bound `T: 'a`... 277 | src/second.rs:62:1: 64:2 note: ...so that the reference type `&'a second::Node` does not outlive the data it points at 278 | src/second.rs:62 pub struct Iter<'a, T> { 279 | src/second.rs:63 next: Option<&'a Node>, 280 | src/second.rs:64 } 281 | error: aborting due to previous error 282 | ``` 283 | 284 | (╯°□°)╯︵ ┻━┻ 285 | 286 | 287 | ```text 288 | rustc --explain E0309 289 | Types in type definitions have lifetimes associated with them that represent 290 | how long the data stored within them is guaranteed to be live. This lifetime 291 | must be as long as the data needs to be alive, and missing the constraint that 292 | denotes this will cause this error. 293 | 294 | // This won't compile because T is not constrained, meaning the data 295 | // stored in it is not guaranteed to last as long as the reference 296 | struct Foo<'a, T> { 297 | foo: &'a T 298 | } 299 | 300 | // This will compile, because it has the constraint on the type parameter 301 | struct Foo<'a, T: 'a> { 302 | foo: &'a T 303 | } 304 | ``` 305 | 306 | This is dumb. I think it's dumb. You have to do it. 307 | 308 | 309 | ```rust 310 | pub struct Iter<'a, T: 'a> { 311 | next: Option<&'a Node>, 312 | } 313 | ``` 314 | 315 | ```text 316 | cargo build 317 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 318 | src/second.rs:67:22: 67:31 error: cannot move out of type `second::List`, which defines the `Drop` trait 319 | src/second.rs:67 Iter { next: self.head.map(|node| &*node) } 320 | ^~~~~~~~~ 321 | src/second.rs:67:44: 67:49 error: `*node` does not live long enough 322 | src/second.rs:67 Iter { next: self.head.map(|node| &*node) } 323 | ^~~~~ 324 | note: in expansion of closure expansion 325 | src/second.rs:67:36: 67:49 note: expansion site 326 | src/second.rs:66:42: 68:6 note: reference must be valid for the lifetime 'a as defined on the block at 66:41... 327 | src/second.rs:66 pub fn iter<'a>(&'a self) -> Iter<'a, T> { 328 | src/second.rs:67 Iter { next: self.head.map(|node| &*node) } 329 | src/second.rs:68 } 330 | src/second.rs:67:43: 67:49 note: ...but borrowed value is only valid for the scope of parameters for function at 67:42 331 | src/second.rs:67 Iter { next: self.head.map(|node| &*node) } 332 | ^~~~~~ 333 | src/second.rs:76:25: 76:29 error: cannot move out of borrowed content 334 | src/second.rs:76 self.next = node.next.map(|node| &*node); 335 | ^~~~ 336 | note: in expansion of closure expansion 337 | src/second.rs:75:23: 78:10 note: expansion site 338 | src/second.rs:76:47: 76:52 error: `*node` does not live long enough 339 | src/second.rs:76 self.next = node.next.map(|node| &*node); 340 | ^~~~~ 341 | note: in expansion of closure expansion 342 | src/second.rs:76:39: 76:52 note: expansion site 343 | note: in expansion of closure expansion 344 | src/second.rs:75:23: 78:10 note: expansion site 345 | src/second.rs:74:46: 79:6 note: reference must be valid for the lifetime 'a as defined on the block at 74:45... 346 | src/second.rs:74 fn next(&mut self) -> Option { 347 | src/second.rs:75 self.next.map(|node| { 348 | src/second.rs:76 self.next = node.next.map(|node| &*node); 349 | src/second.rs:77 &node.elem 350 | src/second.rs:78 }) 351 | src/second.rs:79 } 352 | src/second.rs:76:46: 76:52 note: ...but borrowed value is only valid for the scope of parameters for function at 76:45 353 | src/second.rs:76 self.next = node.next.map(|node| &*node); 354 | ^~~~~~ 355 | error: aborting due to 4 previous errors 356 | ``` 357 | 358 | (ノಥ益ಥ)ノ ┻━┻ 359 | 360 | We forgot `as_ref`: 361 | 362 | ```rust 363 | pub struct Iter<'a, T:'a> { 364 | next: Option<&'a Node>, 365 | } 366 | 367 | impl List { 368 | pub fn iter<'a>(&'a self) -> Iter<'a, T> { 369 | Iter { next: self.head.as_ref().map(|node| &**node) } 370 | } 371 | } 372 | 373 | impl<'a, T> Iterator for Iter<'a, T> { 374 | type Item = &'a T; 375 | 376 | fn next(&mut self) -> Option { 377 | self.next.map(|node| { 378 | self.next = node.next.as_ref().map(|node| &**node); 379 | &node.elem 380 | }) 381 | } 382 | } 383 | ``` 384 | 385 | ```text 386 | lists::cargo build 387 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 388 | ``` 389 | 390 | ┬─┬ ノ( ゜-゜ノ) 391 | 392 | Let's write a test to be sure we didn't no-op it or anything: 393 | 394 | ```rust 395 | #[test] 396 | fn iter() { 397 | let mut list = List::new(); 398 | list.push(1); list.push(2); list.push(3); 399 | 400 | let mut iter = list.iter(); 401 | assert_eq!(iter.next(), Some(&3)); 402 | assert_eq!(iter.next(), Some(&2)); 403 | assert_eq!(iter.next(), Some(&1)); 404 | } 405 | ``` 406 | 407 | ```text 408 | > cargo test 409 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 410 | Running target/debug/lists-5c71138492ad4b4a 411 | 412 | running 5 tests 413 | test first::test::basics ... ok 414 | test second::test::basics ... ok 415 | test second::test::into_iter ... ok 416 | test second::test::iter ... ok 417 | test second::test::peek ... ok 418 | 419 | test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured 420 | 421 | Doc-tests lists 422 | 423 | running 0 tests 424 | 425 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 426 | ``` 427 | 428 | Heck yeah. 429 | 430 | Finally, it should be noted that we actually apply lifetime elision here: 431 | 432 | ```rust 433 | impl List { 434 | pub fn iter<'a>(&'a self) -> Iter<'a, T> { 435 | Iter { next: self.head.as_ref().map(|node| &**node) } 436 | } 437 | } 438 | ``` 439 | 440 | is equivalent to: 441 | 442 | ```rust 443 | impl List { 444 | pub fn iter(&self) -> Iter { 445 | Iter { next: self.head.as_ref().map(|node| &**node) } 446 | } 447 | } 448 | ``` 449 | 450 | Yay less lifetimes! 451 | -------------------------------------------------------------------------------- /second-option.md: -------------------------------------------------------------------------------- 1 | % Using Option 2 | 3 | Particularly observant readers may have noticed that we actually reinvented 4 | a really bad version of Option: 5 | 6 | ```rust 7 | enum Link { 8 | Empty, 9 | More(Box), 10 | } 11 | ``` 12 | 13 | Link is just `Option>`. Now, it's nice not to have to write 14 | `Option>` everywhere, and unlike `pop`, we're not exposing this 15 | to the outside world, so maybe it's fine. However Option has some *really 16 | nice* methods that we've been manually implementing ourselves. Let's *not* 17 | do that, and replace everything with Options. First, we'll do it naively 18 | by just renaming everything to use Some and None: 19 | 20 | ```rust 21 | use std::mem; 22 | 23 | pub struct List { 24 | head: Link, 25 | } 26 | 27 | // yay type aliases! 28 | type Link = Option>; 29 | 30 | struct Node { 31 | elem: i32, 32 | next: Link, 33 | } 34 | 35 | impl List { 36 | pub fn new() -> Self { 37 | List { head: None } 38 | } 39 | 40 | pub fn push(&mut self, elem: i32) { 41 | let new_node = Box::new(Node { 42 | elem: elem, 43 | next: mem::replace(&mut self.head, None), 44 | }); 45 | 46 | self.head = Some(new_node); 47 | } 48 | 49 | pub fn pop(&mut self) -> Option { 50 | match mem::replace(&mut self.head, None) { 51 | None => None, 52 | Some(node) => { 53 | let node = *node; 54 | self.head = node.next; 55 | Some(node.elem) 56 | } 57 | } 58 | } 59 | } 60 | 61 | impl Drop for List { 62 | fn drop(&mut self) { 63 | let mut cur_link = mem::replace(&mut self.head, None); 64 | while let Some(mut boxed_node) = cur_link { 65 | cur_link = mem::replace(&mut boxed_node.next, None); 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | This is marginally better, but the big wins will come from Option's methods. 72 | 73 | First, `mem::replace(&mut option, None)` is such an incredibly 74 | common idiom that Option actually just went ahead and made it a method: `take`. 75 | 76 | ```rust 77 | pub struct List { 78 | head: Link, 79 | } 80 | 81 | type Link = Option>; 82 | 83 | struct Node { 84 | elem: i32, 85 | next: Link, 86 | } 87 | 88 | impl List { 89 | pub fn new() -> Self { 90 | List { head: None } 91 | } 92 | 93 | pub fn push(&mut self, elem: i32) { 94 | let new_node = Box::new(Node { 95 | elem: elem, 96 | next: self.head.take(), 97 | }); 98 | 99 | self.head = Some(new_node); 100 | } 101 | 102 | pub fn pop(&mut self) -> Option { 103 | match self.head.take() { 104 | None => None, 105 | Some(node) => { 106 | let node = *node; 107 | self.head = node.next; 108 | Some(node.elem) 109 | } 110 | } 111 | } 112 | } 113 | 114 | impl Drop for List { 115 | fn drop(&mut self) { 116 | let mut cur_link = self.head.take(); 117 | while let Some(mut boxed_node) = cur_link { 118 | cur_link = boxed_node.next.take(); 119 | } 120 | } 121 | } 122 | ``` 123 | 124 | Second, `match option { None => None, Some(x) => Some(y) }` is such an 125 | incredibly common idiom that it was called `map`. `map` takes a function to 126 | execute on `x` in the `Some(x)` to produce the `y` in `Some(y)`. We could 127 | write a proper `fn` and pass it to `map`, but we'd much rather write what to 128 | do *inline*. 129 | 130 | The way to do this is with a *closure*. Closures are anonymous functions with 131 | an extra super-power: they can refer to local variables *outside* the closure! 132 | This makes them super useful for doing all sorts of conditional logic. The 133 | only place we do a `match` is in `pop`, so let's just rewrite that: 134 | 135 | ```rust 136 | pub fn pop(&mut self) -> Option { 137 | self.head.take().map(|node| { 138 | let node = *node; 139 | self.head = node.next; 140 | node.elem 141 | }) 142 | } 143 | ``` 144 | 145 | Ah, much better. Let's make sure we didn't break anything: 146 | 147 | ```text 148 | > cargo test 149 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 150 | Running target/debug/lists-5c71138492ad4b4a 151 | 152 | running 2 tests 153 | test first::test::basics ... ok 154 | test second::test::basics ... ok 155 | 156 | test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured 157 | 158 | Doc-tests lists 159 | 160 | running 0 tests 161 | 162 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 163 | ``` 164 | 165 | Great! Let's move on to actually improving the code's *behaviour*. 166 | -------------------------------------------------------------------------------- /second-peek.md: -------------------------------------------------------------------------------- 1 | % Peek 2 | 3 | One thing we didn't even bother to implement last time was peeking. Let's go 4 | ahead and do that. All we need to do is return a reference to the element in 5 | the head of the list, if it exists. Sounds easy, let's try: 6 | 7 | ``` 8 | pub fn peek(&self) -> Option<&T> { 9 | self.head.map(|node| { 10 | &node.elem 11 | }) 12 | } 13 | ``` 14 | 15 | 16 | ```text 17 | > cargo build 18 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 19 | src/second.rs:45:9: 45:18 error: cannot move out of type `second::List`, which defines the `Drop` trait 20 | src/second.rs:45 self.head.map(|node| { 21 | ^~~~~~~~~ 22 | src/second.rs:46:14: 46:23 error: `node.elem` does not live long enough 23 | src/second.rs:46 &node.elem 24 | ^~~~~~~~~ 25 | note: in expansion of closure expansion 26 | src/second.rs:45:23: 47:10 note: expansion site 27 | src/second.rs:44:38: 48:6 note: reference must be valid for the anonymous lifetime #1 defined on the block at 44:37... 28 | src/second.rs:44 pub fn peek(&self) -> Option<&T> { 29 | src/second.rs:45 self.head.map(|node| { 30 | src/second.rs:46 &node.elem 31 | src/second.rs:47 }) 32 | src/second.rs:48 } 33 | src/second.rs:45:30: 47:10 note: ...but borrowed value is only valid for the scope of parameters for function at 45:29 34 | src/second.rs:45 self.head.map(|node| { 35 | src/second.rs:46 &node.elem 36 | src/second.rs:47 }) 37 | error: aborting due to 2 previous errors 38 | ``` 39 | 40 | *Sigh*. What now, Rust? 41 | 42 | Map takes `self` by value, which would move the Option out of the thing it's in. 43 | Previously this was fine because we had just `take`n it out, but now we actually 44 | want to leave it where it was. The *correct* way to handle this is with the 45 | `as_ref` method on Option, which has the following definition: 46 | 47 | ```rust 48 | impl Option { 49 | pub fn as_ref(&self) -> Option<&T>; 50 | } 51 | ``` 52 | 53 | It demotes the Option to an Option to a reference to its internals. We could 54 | do this ourselves with an explicit match but *ugh no*. It does mean that we 55 | need to do an extra derefence to cut through the extra indirection, but 56 | thankfully the `.` operator handles that for us. 57 | 58 | 59 | ```rust 60 | pub fn peek(&self) -> Option<&T> { 61 | self.head.as_ref().map(|node| { 62 | &node.elem 63 | }) 64 | } 65 | ``` 66 | 67 | ```text 68 | cargo build 69 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 70 | ``` 71 | 72 | Nailed it. 73 | 74 | We can also make a *mutable* version of this method using `as_mut`: 75 | 76 | ```rust 77 | pub fn peek_mut(&mut self) -> Option<&mut T> { 78 | self.head.as_mut().map(|node| { 79 | &mut node.elem 80 | }) 81 | } 82 | ``` 83 | 84 | ```text 85 | lists::cargo build 86 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 87 | ``` 88 | 89 | EZ 90 | 91 | Don't forget to test it: 92 | 93 | ```rust 94 | #[test] 95 | fn peek() { 96 | let mut list = List::new(); 97 | assert_eq!(list.peek(), None); 98 | assert_eq!(list.peek_mut(), None); 99 | list.push(1); list.push(2); list.push(3); 100 | 101 | assert_eq!(list.peek(), Some(&3)); 102 | assert_eq!(list.peek_mut(), Some(&mut 3)); 103 | } 104 | ``` 105 | 106 | 107 | ``` 108 | cargo test 109 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 110 | Running target/debug/lists-5c71138492ad4b4a 111 | 112 | running 3 tests 113 | test first::test::basics ... ok 114 | test second::test::basics ... ok 115 | test second::test::peek ... ok 116 | 117 | test result: ok. 14 passed; 0 failed; 0 ignored; 0 measured 118 | 119 | Doc-tests lists 120 | 121 | running 0 tests 122 | 123 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 124 | ``` 125 | -------------------------------------------------------------------------------- /second.md: -------------------------------------------------------------------------------- 1 | % An Ok Singly-Linked Stack 2 | 3 | In the previous chapter we wrote up basically a minimum viable singly-linked 4 | stack. However there's a few design decisions that make it kind of sucky. 5 | Let's make it less sucky. In doing so, we will: 6 | 7 | * Deinvent the wheel 8 | * Make our list able to handle any element type 9 | * Add peeking 10 | * Make our list iterable 11 | 12 | And in the process we'll learn about 13 | 14 | * Advanced Option use 15 | * Generics 16 | * Lifetimes 17 | * Iterators 18 | 19 | Let's add a new file called `second.rs`: 20 | 21 | ``` 22 | // in lib.rs 23 | 24 | pub mod first; 25 | pub mod second; 26 | ``` 27 | 28 | And copy everything from `first.rs` into it. 29 | -------------------------------------------------------------------------------- /sixth.md: -------------------------------------------------------------------------------- 1 | % An Ok Unsafe Doubly-Linked Deque 2 | 3 | Coming Soon! 4 | -------------------------------------------------------------------------------- /styles/pdf.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif 3 | } 4 | 5 | pre { 6 | word-break: break-all; 7 | /* Non standard for webkit, but it does work on the Mac when generating the PDF */ 8 | word-break: break-word; 9 | } 10 | -------------------------------------------------------------------------------- /styles/website.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif 3 | } 4 | 5 | -------------------------------------------------------------------------------- /third-arc.md: -------------------------------------------------------------------------------- 1 | % Arc 2 | 3 | One reason to use an immutable linked list is to share data across threads. 4 | After all, shared mutable state is the root of all evil, and one way to solve 5 | that is to kill the *mutable* part forever. 6 | 7 | Except our list isn't thread safe at all. In order to be thread-safe, we need 8 | to fiddle with reference counts *atomically*. Otherwise, two threads could 9 | try to increment the reference count, *and only one would happen*. Then the 10 | list could get freed too soon! 11 | 12 | In order to get thread safety, we have to use *Arc*. Arc is completely identical 13 | to Rc except for the fact that reference counts are modified atomically. This 14 | has a bit of overhead if you don't need it, so Rust exposes both. 15 | All we need to do to make our list is replace every reference to Rc with 16 | `std::sync::Arc`. That's it. We're thread safe. Done! 17 | 18 | But this raises an interesting question: how do we *know* if a type is 19 | thread-safe or not? Can we accidentally mess up? 20 | 21 | No! You can't mess up thread-safety in Rust! 22 | 23 | The reason this is the case is because Rust models thread-safety in a 24 | first-class way with two traits: `Send` and `Sync`. 25 | 26 | A type is *Send* if it's safe to *move* to another thread. A type is *Sync* if 27 | it's safe to *share* between multiple threads. That is, if `T` is Sync, `&T` is 28 | Send. Safe in this case means it's impossible to to cause *data races*, (not to 29 | be mistaken with the more general issue of *race conditions*). 30 | 31 | These are marker traits, which is a fancy way of saying they're traits that 32 | provide absolutely no interface. You either *are* Send, or you aren't. It's just 33 | a property *other* APIs can require. If you aren't appropriately Send, 34 | then it's statically impossible to be sent to a different thread! Sweet! 35 | 36 | Send and Sync are also automatically derived traits based on whether you are 37 | totally composed of Send and Sync types. It's similar to how you can only 38 | implement Copy if you're only made of Copy types, but then we just go ahead 39 | and implement it automatically if you are. 40 | 41 | Almost every type is Send and Sync. Most types are Send because they totally 42 | own their data. Most types are Sync because the only way to share data across 43 | threads is to put them behind a shared reference, which makes them immutable! 44 | 45 | However there are special types that violate these properties: those that have 46 | *interior mutability*. So far we've only really interacted with *inherited 47 | mutability* (AKA external mutability): the mutability of a value is inherited 48 | from the mutability of its container. That is, you can't just randomly mutate 49 | some field of a non-mutable type because you feel like it. 50 | 51 | Interior mutability types violate this: they let you mutate through a shared 52 | reference. There are two major classes of interior mutability: cells, which 53 | only work in a single-threaded context; and locks, which work in a 54 | multi-threaded context. For obvious reasons, cells are cheaper when you can 55 | use them. There's also atomics, which are primitives that act like a lock. 56 | 57 | So what does all of this have to do with Rc and Arc? Well, they both use 58 | interior mutability for their *reference count*. Worse, this reference count 59 | is shared between every instance! Rc just uses a cell, which means it's not 60 | thread safe. Arc uses an atomic, which means it *is* thread safe. Of course, 61 | you can't magically make a type thread safe by putting it in Arc. Arc only can 62 | derive thread-safety like any other types. 63 | 64 | I really really really don't want to get into the finer details of atomic 65 | memory models or non-derived Send implementations. Needless to say, as you get 66 | deeper into Rust's thread-safety story, stuff gets more complicated. As a 67 | high-level consumer, it all *just works* and you don't really need to think 68 | about it. 69 | -------------------------------------------------------------------------------- /third-basics.md: -------------------------------------------------------------------------------- 1 | % Basics 2 | 3 | We already know a lot of the basics of Rust now, so we can do a lot of the 4 | simple stuff again. 5 | 6 | For the constructor, we can again just copy-paste: 7 | 8 | ``` 9 | impl List { 10 | pub fn new() -> Self { 11 | List { head: None } 12 | } 13 | } 14 | ``` 15 | 16 | `push` and `pop` don't really make sense anymore. Instead we can provide 17 | `append` and `tail`, which provide approximately the same thing. 18 | 19 | Let's start with appending. It takes a list and an element, and returns a 20 | List. Like the mutable list case, we want to make a new node, that has the old 21 | list as its `next` value. The only novel thing is how to *get* that next value, 22 | because we're not allowed to mutate anything. 23 | 24 | The answer to our prayers is the Clone trait. Clone is implemented by almost 25 | every type, and provides a generic way to get "another one like this one" that 26 | is logically disjoint given only a shared reference. It's like a copy 27 | constructor in C++, but it's never implicitly invoked. 28 | 29 | Rc in particular uses Clone as the way to increment the reference count. So 30 | rather than moving a Box to be in the sublist, we just clone the head of the 31 | old list. We don't even need to match on the head, because Option exposes a 32 | Clone implementation that does exactly the thing we want. 33 | 34 | Alright, let's give it a shot: 35 | 36 | ```rust 37 | pub fn append(&self, elem: T) -> List { 38 | List { head: Some(Rc::new(Node { 39 | elem: elem, 40 | next: self.head.clone(), 41 | }))} 42 | } 43 | ``` 44 | 45 | ```text 46 | > cargo build 47 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 48 | src/third.rs:10:5: 10:12 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 49 | src/third.rs:10 elem: T, 50 | ^~~~~~~ 51 | src/third.rs:11:5: 11:18 warning: struct field is never used: `next`, #[warn(dead_code)] on by default 52 | src/third.rs:11 next: Link, 53 | ^~~~~~~~~~~~~ 54 | ``` 55 | 56 | Wow, Rust is really hard-nosed about actually using fields. It can tell no 57 | consumer can ever actually observe the use of these fields! Still, we seem good 58 | so far. 59 | 60 | `tail` is the logical inverse of this operation. It takes a list and removes the 61 | whole list with the first element removed. All that is is cloning the *second* 62 | element in the list (if it exists). Let's try this: 63 | 64 | ```rust 65 | pub fn tail(&self) -> List { 66 | List { head: self.head.map(|node| node.next.clone()) } 67 | } 68 | ``` 69 | 70 | ```text 71 | cargo build 72 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 73 | src/third.rs:28:22: 28:61 error: mismatched types: 74 | expected `core::option::Option>>`, 75 | found `core::option::Option>>>` 76 | (expected struct `alloc::rc::Rc`, 77 | found enum `core::option::Option`) [E0308] 78 | src/third.rs:28 List { head: self.head.as_ref().map(|node| node.next.clone()) } 79 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 80 | src/third.rs:28:22: 28:61 help: run `rustc --explain E0308` to see a detailed explanation 81 | error: aborting due to previous error 82 | ``` 83 | 84 | Hrm, we messed up. `map` expects us to return a Y, but here we're returning an 85 | `Option`. Thankfully, this is another common Option pattern, and we can just 86 | use `and_then` to let us return an Option. 87 | 88 | ```rust 89 | pub fn tail(&self) -> List { 90 | List { head: self.head.as_ref().and_then(|node| node.next.clone()) } 91 | } 92 | ``` 93 | 94 | ```text 95 | > cargo build 96 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 97 | ``` 98 | 99 | Great. 100 | 101 | Now that we have `tail`, we should probably provide `head`, which returns a 102 | reference to the first element. That's just `peek` from the mutable list: 103 | 104 | ```rust 105 | pub fn head(&self) -> Option<&T> { 106 | self.head.as_ref().map(|node| &node.elem ) 107 | } 108 | ``` 109 | 110 | ```text 111 | > cargo build 112 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 113 | ``` 114 | 115 | Nice. 116 | 117 | That's enough functionality that we can test it: 118 | 119 | 120 | ```rust 121 | #[cfg(test)] 122 | mod test { 123 | use super::List; 124 | 125 | #[test] 126 | fn basics() { 127 | let list = List::new(); 128 | assert_eq!(list.head(), None); 129 | 130 | let list = list.append(1).append(2).append(3); 131 | assert_eq!(list.head(), Some(&3)); 132 | 133 | let list = list.tail(); 134 | assert_eq!(list.head(), Some(&2)); 135 | 136 | let list = list.tail(); 137 | assert_eq!(list.head(), Some(&1)); 138 | 139 | let list = list.tail(); 140 | assert_eq!(list.head(), None); 141 | 142 | // Make sure empty tail works 143 | let list = list.tail(); 144 | assert_eq!(list.head(), None); 145 | 146 | } 147 | } 148 | ``` 149 | 150 | ```text 151 | > cargo test 152 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 153 | Running target/debug/lists-5c71138492ad4b4a 154 | 155 | running 5 tests 156 | test first::test::basics ... ok 157 | test second::test::into_iter ... ok 158 | test second::test::basics ... ok 159 | test second::test::iter ... ok 160 | test third::test::basics ... ok 161 | 162 | test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured 163 | 164 | Doc-tests lists 165 | 166 | running 0 tests 167 | 168 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 169 | ``` 170 | 171 | Perfect! 172 | 173 | Iter is identical to the mutable list case: 174 | 175 | ```rust 176 | pub struct Iter<'a, T:'a> { 177 | next: Option<&'a Node>, 178 | } 179 | 180 | impl List { 181 | pub fn iter<'a>(&'a self) -> Iter<'a, T> { 182 | Iter { next: self.head.as_ref().map(|node| &**node) } 183 | } 184 | } 185 | 186 | impl<'a, T> Iterator for Iter<'a, T> { 187 | type Item = &'a T; 188 | 189 | fn next(&mut self) -> Option { 190 | self.next.map(|node| { 191 | self.next = node.next.as_ref().map(|node| &**node); 192 | &node.elem 193 | }) 194 | } 195 | } 196 | ``` 197 | 198 | ```rust 199 | #[test] 200 | fn iter() { 201 | let list = List::new().append(1).append(2).append(3); 202 | 203 | let mut iter = list.iter(); 204 | assert_eq!(iter.next(), Some(&3)); 205 | assert_eq!(iter.next(), Some(&2)); 206 | assert_eq!(iter.next(), Some(&1)); 207 | } 208 | ``` 209 | 210 | ```text 211 | cargo test 212 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 213 | Running target/debug/lists-5c71138492ad4b4a 214 | 215 | running 7 tests 216 | test first::test::basics ... ok 217 | test second::test::basics ... ok 218 | test second::test::iter ... ok 219 | test second::test::into_iter ... ok 220 | test second::test::peek ... ok 221 | test third::test::basics ... ok 222 | test third::test::iter ... ok 223 | 224 | test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured 225 | 226 | Doc-tests lists 227 | 228 | running 0 tests 229 | 230 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 231 | ``` 232 | 233 | Who ever said dynamic typing was easier? 234 | 235 | (chumps did) 236 | 237 | Note that we can't implement IntoIter or IterMut for this type. We only have 238 | shared access to elements. 239 | -------------------------------------------------------------------------------- /third-drop.md: -------------------------------------------------------------------------------- 1 | % Drop 2 | 3 | Like the mutable lists, we have a the recursive destructor problem. 4 | Admittedly, this isn't as bad of a problem for the immutable list: if we ever 5 | hit another node that's the head of another list *somewhere*, we won't 6 | recursively drop it. However it's still a thing we should care about, and 7 | how to deal with isn't as clear. Here's how we solved it before: 8 | 9 | ```rust 10 | impl Drop for List { 11 | fn drop(&mut self) { 12 | let mut cur_link = self.head.take(); 13 | while let Some(mut boxed_node) = cur_link { 14 | cur_link = boxed_node.next.take(); 15 | } 16 | } 17 | } 18 | ``` 19 | 20 | The problem is the body of the loop: 21 | 22 | ```rust 23 | cur_link = boxed_node.next.take(); 24 | ``` 25 | 26 | This is mutating the Node inside the Box, but we can't do that with Rc; it only 27 | gives us shared access. There's two ways to handle this. 28 | 29 | The first way is that we can keep grabbing the tail of the list and dropping the 30 | previous one to decrement its count. This will prevent the old list from 31 | recursively dropping the rest of the list because we hold an outstanding 32 | reference to it. This has the unfortunate problem that we traverse the *entire* 33 | list whenever we drop it. In particular this means building a list of length 34 | n in place takes O(n2) as we traverse a lists of length `n-1`, 35 | `n-2`, .., `1` to guard against overflow (this is really really really 36 | really bad). 37 | 38 | The second way is if we could identify that we're the last list that knows 39 | about this node, we could in *principle* actually move the Node out of the Rc. 40 | Then we could also know when to stop: whenver we *can't* hoist out the Node. 41 | For reference, the unstable function is called `try_unwrap`. 42 | 43 | Rc actually lets you do this... but only in nightly Rust. Honestly, I'd rather 44 | risk blowing the stack sometimes than iterate every list whenever it gets 45 | dropped. Still if you'd rather not blow the stack, here's the first 46 | (O(n)) solution: 47 | 48 | ```rust 49 | impl Drop for List { 50 | fn drop(&mut self) { 51 | // Steal the list's head 52 | let mut cur_list = self.head.take(); 53 | while let Some(node) = cur_list { 54 | // Clone the current node's next node. 55 | cur_list = node.next.clone(); 56 | // Node dropped here. If the old node had 57 | // refcount 1, then it will be dropped and freed, but it won't 58 | // be able to fully recurse and drop its child, because we 59 | // hold another Rc to it. 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | and here's the second (amortized O(1)) solution (only works on nightly): 66 | 67 | ```rust 68 | impl Drop for List { 69 | fn drop(&mut self) { 70 | let mut head = self.head.take(); 71 | while let Some(node) = head { 72 | if let Ok(mut node) = Rc::try_unwrap(node) { 73 | head = node.next.take(); 74 | } else { 75 | break; 76 | } 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | -------------------------------------------------------------------------------- /third-final.md: -------------------------------------------------------------------------------- 1 | % Final Code 2 | 3 | That's all I really have to say on the immutable stack. We're getting pretty 4 | good at implementing lists now! 5 | 6 | ```rust 7 | use std::rc::Rc; 8 | 9 | pub struct List { 10 | head: Link, 11 | } 12 | 13 | type Link = Option>>; 14 | 15 | struct Node { 16 | elem: T, 17 | next: Link, 18 | } 19 | 20 | pub struct Iter<'a, T:'a> { 21 | next: Option<&'a Node>, 22 | } 23 | 24 | 25 | 26 | 27 | impl List { 28 | pub fn new() -> Self { 29 | List { head: None } 30 | } 31 | 32 | pub fn append(&self, elem: T) -> List { 33 | List { head: Some(Rc::new(Node { 34 | elem: elem, 35 | next: self.head.clone(), 36 | }))} 37 | } 38 | 39 | pub fn tail(&self) -> List { 40 | List { head: self.head.as_ref().and_then(|node| node.next.clone()) } 41 | } 42 | 43 | pub fn head(&self) -> Option<&T> { 44 | self.head.as_ref().map(|node| &node.elem) 45 | } 46 | 47 | pub fn iter(&self) -> Iter { 48 | Iter { next: self.head.as_ref().map(|node| &**node) } 49 | } 50 | } 51 | 52 | impl<'a, T> Iterator for Iter<'a, T> { 53 | type Item = &'a T; 54 | 55 | fn next(&mut self) -> Option { 56 | self.next.map(|node| { 57 | self.next = node.next.as_ref().map(|node| &**node); 58 | &node.elem 59 | }) 60 | } 61 | } 62 | 63 | 64 | 65 | #[cfg(test)] 66 | mod test { 67 | use super::List; 68 | 69 | #[test] 70 | fn basics() { 71 | let list = List::new(); 72 | assert_eq!(list.head(), None); 73 | 74 | let list = list.append(1).append(2).append(3); 75 | assert_eq!(list.head(), Some(&3)); 76 | 77 | let list = list.tail(); 78 | assert_eq!(list.head(), Some(&2)); 79 | 80 | let list = list.tail(); 81 | assert_eq!(list.head(), Some(&1)); 82 | 83 | let list = list.tail(); 84 | assert_eq!(list.head(), None); 85 | 86 | // Make sure empty tail works 87 | let list = list.tail(); 88 | assert_eq!(list.head(), None); 89 | 90 | } 91 | 92 | #[test] 93 | fn iter() { 94 | let list = List::new().append(1).append(2).append(3); 95 | 96 | let mut iter = list.iter(); 97 | assert_eq!(iter.next(), Some(&3)); 98 | assert_eq!(iter.next(), Some(&2)); 99 | assert_eq!(iter.next(), Some(&1)); 100 | } 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /third-layout.md: -------------------------------------------------------------------------------- 1 | % Layout 2 | 3 | Alright, back to the drawing board on layout. 4 | 5 | The most important thing about 6 | a persistent list is that you can manipulate the tails of lists basically 7 | for free: 8 | 9 | For instance, this isn't an uncommon workload to see with a persistent list: 10 | 11 | ```text 12 | list1 = A -> B -> C -> D 13 | list2 = tail(list1) = B -> C -> D 14 | list3 = push(list2, X) = X -> B -> C -> D 15 | ``` 16 | 17 | But at the end we want the memory to look like this: 18 | 19 | ```text 20 | list1 -> A ---v 21 | list2 ------> B -> C -> D 22 | list3 -> X ---^ 23 | ``` 24 | 25 | This just can't work with Boxes, because ownership of `B` is *shared*. Who 26 | should free it? If I drop list2, does it free B? With boxes we certainly would 27 | expect so! 28 | 29 | Functional languages -- and indeed almost every other language -- get away with 30 | this by using *garbage collection*. With the magic of garbage collection, B will 31 | be freed only after everyone stops looking at it. Hooray! 32 | 33 | Rust doesn't have anything like the garbage collectors these languages have. 34 | They have *tracing* GC, which will dig through all the memory that's sitting 35 | around at runtime and figure out what's garbage automatically. Instead, all 36 | Rust has today is *reference counting*. Reference counting is basically a 37 | poor-man's GC. For many workloads, it has significantly less throughput 38 | than a tracing collector, and it completely falls over if you manage to 39 | build cycles. Thankfully, for our usecase we'll never run into cycles 40 | (feel free to try to prove this to yourself -- I sure won't). 41 | 42 | So how do we do reference counted garbage collection? `Rc`! Rc is just like 43 | Box, but we can duplicate it, and its memory will *only* be freed when *all* 44 | the Rc's derived from are dropped. Unforuntately, this flexibility comes at 45 | a serious cost: we can only Deref an Rc. No DerefMut or DerefMove. This means 46 | we can't ever really get data out of one of our lists, nor can we mutate them. 47 | 48 | So what's our layout gonna look like? Well, previously we had: 49 | 50 | ```rust 51 | pub struct List { 52 | head: Link, 53 | } 54 | 55 | type Link = Option>>; 56 | 57 | struct Node { 58 | elem: T, 59 | next: Link, 60 | } 61 | ``` 62 | 63 | Can we just change Box to Rc? 64 | 65 | ```rust 66 | // in third.rs 67 | 68 | pub struct List { 69 | head: Link, 70 | } 71 | 72 | type Link = Option>>; 73 | 74 | struct Node { 75 | elem: T, 76 | next: Link, 77 | } 78 | ``` 79 | 80 | ```text 81 | cargo build 82 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 83 | src/third.rs:5:23: 5:34 error: use of undeclared type name `Rc` [E0412] 84 | src/third.rs:5 type Link = Option>>; 85 | ^~~~~~~~~~~ 86 | error: aborting due to previous error 87 | ``` 88 | 89 | Oh dang, sick burn. Unlike everything we used for our mutable lists, Rc is so 90 | lame that it's not even implicitly imported into every single Rust program. 91 | *What a loser*. 92 | 93 | ```rust 94 | use std::rc::Rc; 95 | ``` 96 | 97 | ```text 98 | cargo build 99 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 100 | src/third.rs:4:5: 4:18 warning: struct field is never used: `head`, #[warn(dead_code)] on by default 101 | src/third.rs:4 head: Link, 102 | ^~~~~~~~~~~~~ 103 | src/third.rs:10:5: 10:12 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 104 | src/third.rs:10 elem: T, 105 | ^~~~~~~ 106 | src/third.rs:11:5: 11:18 warning: struct field is never used: `next`, #[warn(dead_code)] on by default 107 | src/third.rs:11 next: Link, 108 | ^~~~~~~~~~~~~ 109 | ``` 110 | 111 | Seems legit. Rust continues to be a complete joke to write. I bet we can just 112 | find-and-replace Box with Rc and call it a day. 113 | 114 | ... 115 | 116 | No. No we can't. 117 | -------------------------------------------------------------------------------- /third.md: -------------------------------------------------------------------------------- 1 | % A Persistent Singly-Linked Stack 2 | 3 | Alright, we've mastered the art of mutable singly-linked stacks. 4 | 5 | Let's move from *single* ownership to *shared* ownership by writing a 6 | *persistent* immutable singly linked list. This will be exactly the list 7 | that functional programmers have come to know and love. You can get the 8 | head *or* the tail and put someone's head on someone else's tail... 9 | and... that's basically it. Immutability is a hell of a drug. 10 | 11 | In the process we'll largely just become familiar with Rc and Arc, but this 12 | will set us up for the next list which will *change the game*. 13 | 14 | Let's add a new file called `third.rs`: 15 | 16 | ``` 17 | // in lib.rs 18 | 19 | pub mod first; 20 | pub mod second; 21 | pub mod third; 22 | ``` 23 | 24 | No copy-pasta this time. This is a clean room operation. 25 | --------------------------------------------------------------------------------