├── .gitignore ├── lists ├── .gitignore ├── Cargo.toml └── src │ ├── lib.rs │ ├── first.rs │ ├── third.rs │ ├── silly1.rs │ ├── second.rs │ ├── fifth.rs │ └── fourth.rs ├── indy.gif ├── text ├── sixth.md ├── infinity.md ├── first.md ├── second.md ├── fourth.md ├── third.md ├── fifth.md ├── first-new.md ├── SUMMARY.md ├── first-ownership.md ├── first-final.md ├── third-final.md ├── fifth-unsafe.md ├── second-into-iter.md ├── fourth-layout.md ├── third-drop.md ├── third-arc.md ├── second-generic.md ├── first-drop.md ├── first-push.md ├── second-peek.md ├── third-layout.md ├── first-test.md ├── fifth-extras.md ├── second-option.md ├── fourth-symmetry.md ├── second-final.md ├── fifth-final.md ├── second-iter-mut.md ├── third-basics.md ├── infinity-double-single.md ├── fourth-final.md ├── fourth-peek.md ├── fourth-building.md ├── fourth-breaking.md ├── fifth-basics.md ├── fourth-iteration.md └── first-layout.md ├── FiraSans-Medium.woff ├── FiraSans-Regular.woff ├── Heuristica-Italic.woff ├── SourceCodePro-Regular.woff ├── SourceSerifPro-Bold.woff ├── SourceCodePro-Semibold.woff ├── SourceSerifPro-Regular.woff ├── .travis.yml ├── license-MIT ├── README.md └── rust.css /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | text/_book 3 | -------------------------------------------------------------------------------- /lists/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /indy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ble/too-many-lists/master/indy.gif -------------------------------------------------------------------------------- /text/sixth.md: -------------------------------------------------------------------------------- 1 | % An Ok Unsafe Doubly-Linked Deque 2 | 3 | Coming Soon! 4 | -------------------------------------------------------------------------------- /FiraSans-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ble/too-many-lists/master/FiraSans-Medium.woff -------------------------------------------------------------------------------- /FiraSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ble/too-many-lists/master/FiraSans-Regular.woff -------------------------------------------------------------------------------- /Heuristica-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ble/too-many-lists/master/Heuristica-Italic.woff -------------------------------------------------------------------------------- /lists/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lists" 3 | version = "0.1.0" 4 | authors = ["ABeingessner"] 5 | -------------------------------------------------------------------------------- /SourceCodePro-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ble/too-many-lists/master/SourceCodePro-Regular.woff -------------------------------------------------------------------------------- /SourceSerifPro-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ble/too-many-lists/master/SourceSerifPro-Bold.woff -------------------------------------------------------------------------------- /SourceCodePro-Semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ble/too-many-lists/master/SourceCodePro-Semibold.woff -------------------------------------------------------------------------------- /SourceSerifPro-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ble/too-many-lists/master/SourceSerifPro-Regular.woff -------------------------------------------------------------------------------- /lists/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rc_unique, cell_extras)] 2 | 3 | pub mod first; 4 | pub mod second; 5 | pub mod third; 6 | pub mod fourth; 7 | pub mod fifth; 8 | 9 | pub mod silly1; 10 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/first.md: -------------------------------------------------------------------------------- 1 | % A Bad Singly-Linked Stack 2 | 3 | This one's gonna be *by far* the longest, as we need to introduce basically 4 | all of Rust, and are gonna build up some things "the hard way" to better 5 | understand the language. 6 | 7 | We'll put our first list in `src/first.rs`. We need to tell Rust that `first.rs` is 8 | something that our lib uses. All that requires is that we put this at the top of 9 | `src/lib.rs` (which Cargo made for us): 10 | 11 | ```rust 12 | // in lib.rs 13 | pub mod first; 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | 5 | addons: 6 | apt: 7 | packages: 8 | - tree 9 | 10 | install: 11 | - git clone --depth 1 https://github.com/steveklabnik/rustbook.git 12 | - cd rustbook && cargo build --release && cd .. 13 | 14 | script: 15 | - rustbook/target/release/rustbook build text/ book/ 16 | 17 | after_success: 18 | - tree . 19 | - zip too-many-lists.zip rust.css 20 | - zip -r too-many-lists.zip book 21 | 22 | deploy: 23 | provider: releases 24 | api_key: "$GH_DEPLOY_TOKEN" 25 | file: "too-many-lists.zip" 26 | skip_cleanup: true 27 | on: 28 | tags: true 29 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 if *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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /license-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Alexis Beingessner 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /text/first-new.md: -------------------------------------------------------------------------------- 1 | % New 2 | 3 | To associate actual code with a type, we use `impl` blocks: 4 | 5 | ```rust 6 | impl List { 7 | // TODO, make code happen 8 | } 9 | ``` 10 | 11 | Now we just need to figure out how to actually write code. In Rust we declare 12 | a function like so: 13 | 14 | ```rust 15 | fn foo(arg1: Type1, arg2: Type2) -> ReturnType { 16 | // body 17 | } 18 | ``` 19 | 20 | The first thing we want is a way to *construct* a list. Since we hide the 21 | implementation details, we need to provide that as a function. The usual way 22 | to do that in Rust is to provide a static method, which is just a 23 | normal function inside an `impl`: 24 | 25 | ```rust 26 | impl List { 27 | pub fn new() -> Self { 28 | List { head: Link::Empty } 29 | } 30 | } 31 | ``` 32 | 33 | A few notes on this: 34 | 35 | * Self is an alias for "that type I wrote at top next to `impl`". Great for 36 | not repeating yourself! 37 | * We create an instance of a struct in much the same way we declare it, except 38 | instead of providing the types of its fields, we initialize them with values. 39 | * We refer to variants of an enum using `::`, which is the namespacing operator. 40 | * The last expression of a function is implicitly returned. 41 | This makes simple functions a little neater. You can still use `return` 42 | to return early like other C-like languages. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /text/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [A Bad Stack](first.md) 4 | * [Layout](first-layout.md) 5 | * [New](first-new.md) 6 | * [Ownership 101](first-ownership.md) 7 | * [Push](first-push.md) 8 | * [Pop](first-pop.md) 9 | * [Testing](first-test.md) 10 | * [Drop](first-drop.md) 11 | * [Final Code](first-final.md) 12 | * [An Ok Stack](second.md) 13 | * [Option](second-option.md) 14 | * [Generic](second-generic.md) 15 | * [Peek](second-peek.md) 16 | * [IntoIter](second-into-iter.md) 17 | * [Iter](second-iter.md) 18 | * [IterMut](second-iter-mut.md) 19 | * [Final Code](second-final.md) 20 | * [A Persistent Stack](third.md) 21 | * [Layout](third-layout.md) 22 | * [Basics](third-basics.md) 23 | * [Drop](third-drop.md) 24 | * [Arc](third-arc.md) 25 | * [Final Code](third-final.md) 26 | * [A Bad Safe Deque](fourth.md) 27 | * [Layout](fourth-layout.md) 28 | * [Building](fourth-building.md) 29 | * [Breaking](fourth-breaking.md) 30 | * [Peek](fourth-peek.md) 31 | * [Symmetric Cases](fourth-symmetry.md) 32 | * [Iteration](fourth-iteration.md) 33 | * [Final Code](fourth-final.md) 34 | * [An Unsafe Queue](fifth.md) 35 | * [Layout](fifth-layout.md) 36 | * [Unsafe](fifth-unsafe.md) 37 | * [Basics](fifth-basics.md) 38 | * [Extras](fifth-extras.md) 39 | * [Final Code](fifth-final.md) 40 | * [An Ok Unsafe Deque](sixth.md) 41 | * [A Bunch of Silly Lists](infinity.md) 42 | * [The Double Single](infinity-double-single.md) 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn Rust by writing Entirely Too Many linked lists [![Build Status](https://travis-ci.org/rust-unofficial/too-many-lists.svg?branch=master)](https://travis-ci.org/rust-unofficial/too-many-lists) 2 | 3 | Read the pretty version at http://cglab.ca/~abeinges/blah/too-many-lists/book/ 4 | 5 | # Building 6 | 7 | Building requires an instance of rustbook be set up on your machine. 8 | 9 | A mirror of the rustbook code can be found [here](https://github.com/steveklabnik/rustbook). 10 | This requires a nightly version of the Rust compiler, as well as Cargo: 11 | 12 | ```sh 13 | cd rustbook/ 14 | cargo build --release 15 | ``` 16 | 17 | Once built, the binary can be found at `rustbook/target/release/rustbook`. 18 | 19 | --- 20 | 21 | If that doesn't work (#13), rustbook can also be built as part of rustc. 22 | Here's instructions for 23 | [building Rust from source](https://github.com/rust-lang/rust/#building-from-source). 24 | 25 | However it needs to be a slight deviation from the normal process: 26 | 27 | * You should do `./configure --enable-rpath` instead of `./configure` 28 | * You don't need to `install` (I don't think rustbook will use that -- although 29 | maybe I'm wrong and make install will install rustbook too -- happy to be wrong!) 30 | 31 | Once built, rustbook will be somewhere deep in the build target 32 | directories. This is a bit platform-specific, to be honest. On my 33 | machine it's at `x86_64-apple-darwin/stage2/bin/rustbook`. The 34 | `x86_64-apple-darwin` bit is the *really* platform specific part, 35 | where I hope you can guess what your platform will sort of look 36 | like. On windows you may need to look in stage3. 37 | 38 | Now just copy or link rustbook to be somewhere on your path. 39 | 40 | --- 41 | 42 | Once you have the rustbook binary, you just need to do: 43 | 44 | ```sh 45 | cd too-many-lists/ 46 | rm -rf book/ && rustbook build text/ book/ 47 | ``` 48 | -------------------------------------------------------------------------------- /lists/src/first.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | pub struct List { 4 | head: Link, 5 | } 6 | 7 | enum Link { 8 | Empty, 9 | More(Box), 10 | } 11 | 12 | struct Node { 13 | elem: i32, 14 | next: Link, 15 | } 16 | 17 | impl List { 18 | pub fn new() -> Self { 19 | List { head: Link::Empty } 20 | } 21 | 22 | pub fn push(&mut self, elem: i32) { 23 | let new_node = Box::new(Node { 24 | elem: elem, 25 | next: mem::replace(&mut self.head, Link::Empty), 26 | }); 27 | 28 | self.head = Link::More(new_node); 29 | } 30 | 31 | pub fn pop(&mut self) -> Option { 32 | match mem::replace(&mut self.head, Link::Empty) { 33 | Link::Empty => None, 34 | Link::More(node) => { 35 | let node = *node; 36 | self.head = node.next; 37 | Some(node.elem) 38 | } 39 | } 40 | } 41 | } 42 | 43 | impl Drop for List { 44 | fn drop(&mut self) { 45 | let mut cur_link = mem::replace(&mut self.head, Link::Empty); 46 | while let Link::More(mut boxed_node) = cur_link { 47 | cur_link = mem::replace(&mut boxed_node.next, Link::Empty); 48 | } 49 | } 50 | } 51 | 52 | #[cfg(test)] 53 | mod test { 54 | use super::List; 55 | 56 | #[test] 57 | fn basics() { 58 | let mut list = List::new(); 59 | 60 | // Check empty list behaves right 61 | assert_eq!(list.pop(), None); 62 | 63 | // Populate list 64 | list.push(1); 65 | list.push(2); 66 | list.push(3); 67 | 68 | // Check normal removal 69 | assert_eq!(list.pop(), Some(3)); 70 | assert_eq!(list.pop(), Some(2)); 71 | 72 | // Push some more just to make sure nothing's corrupted 73 | list.push(4); 74 | list.push(5); 75 | 76 | // Check normal removal 77 | assert_eq!(list.pop(), Some(5)); 78 | assert_eq!(list.pop(), Some(4)); 79 | 80 | // Check exhaustion 81 | assert_eq!(list.pop(), Some(1)); 82 | assert_eq!(list.pop(), None); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lists/src/third.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | pub struct List { 4 | head: Link, 5 | } 6 | 7 | type Link = Option>>; 8 | 9 | struct Node { 10 | elem: T, 11 | next: Link, 12 | } 13 | 14 | pub struct Iter<'a, T:'a> { 15 | next: Option<&'a Node>, 16 | } 17 | 18 | 19 | 20 | 21 | impl List { 22 | pub fn new() -> Self { 23 | List { head: None } 24 | } 25 | 26 | pub fn append(&self, elem: T) -> List { 27 | List { head: Some(Rc::new(Node { 28 | elem: elem, 29 | next: self.head.clone(), 30 | }))} 31 | } 32 | 33 | pub fn tail(&self) -> List { 34 | List { head: self.head.as_ref().and_then(|node| node.next.clone()) } 35 | } 36 | 37 | pub fn head(&self) -> Option<&T> { 38 | self.head.as_ref().map(|node| &node.elem) 39 | } 40 | 41 | pub fn iter(&self) -> Iter { 42 | Iter { next: self.head.as_ref().map(|node| &**node) } 43 | } 44 | } 45 | 46 | impl<'a, T> Iterator for Iter<'a, T> { 47 | type Item = &'a T; 48 | 49 | fn next(&mut self) -> Option { 50 | self.next.map(|node| { 51 | self.next = node.next.as_ref().map(|node| &**node); 52 | &node.elem 53 | }) 54 | } 55 | } 56 | 57 | 58 | 59 | #[cfg(test)] 60 | mod test { 61 | use super::List; 62 | 63 | #[test] 64 | fn basics() { 65 | let list = List::new(); 66 | assert_eq!(list.head(), None); 67 | 68 | let list = list.append(1).append(2).append(3); 69 | assert_eq!(list.head(), Some(&3)); 70 | 71 | let list = list.tail(); 72 | assert_eq!(list.head(), Some(&2)); 73 | 74 | let list = list.tail(); 75 | assert_eq!(list.head(), Some(&1)); 76 | 77 | let list = list.tail(); 78 | assert_eq!(list.head(), None); 79 | 80 | // Make sure empty tail works 81 | let list = list.tail(); 82 | assert_eq!(list.head(), None); 83 | 84 | } 85 | 86 | #[test] 87 | fn iter() { 88 | let list = List::new().append(1).append(2).append(3); 89 | 90 | let mut iter = list.iter(); 91 | assert_eq!(iter.next(), Some(&3)); 92 | assert_eq!(iter.next(), Some(&2)); 93 | assert_eq!(iter.next(), Some(&1)); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /text/first-ownership.md: -------------------------------------------------------------------------------- 1 | % Ownership 101 2 | 3 | Now that we can construct a list, it'd be nice to be able to *do* something 4 | with it. We do that with "normal" (non-static) methods. Methods are a special 5 | case of function in Rust because of the `self` argument, which doesn't have 6 | a declared type: 7 | 8 | ```rust,ignore 9 | fn foo(self, arg2: Type2) -> ReturnType { 10 | // body 11 | } 12 | ``` 13 | 14 | There's 3 primary forms that self can take: `self`, `&mut self`, and `&self`. 15 | These 3 forms represent the three primary forms of ownership in Rust: 16 | 17 | * `self` - Value 18 | * `&mut self` - mutable reference 19 | * `&self` - shared reference 20 | 21 | A value represents *true* ownership. You can do whatever you want with a value: 22 | move it, destroy it, mutate it, or loan it out via a reference. When you pass 23 | something by value, it's *moved* to the new location. The new location now 24 | owns the value, and the old location can no longer access it. For this reason 25 | most methods don't want `self` -- it would be pretty lame if trying to work with 26 | a list made it go away! 27 | 28 | A mutable reference represents temporary *exclusive access* to a value that you 29 | don't own. You're allowed to do absolutely anything you want to a value you 30 | have a mutable reference to as long as when your loan expires, wherever you 31 | loaned it from still sees a valid value. This means you can actually completely 32 | overwrite the value. A really useful special case of this is *swapping* a value 33 | out for another, which we'll be using a lot. The only thing you can't do with an 34 | `&mut` is move the value out with no replacement. `&mut self` is great for 35 | methods that want to mutate `self`. 36 | 37 | A shared reference represents temporary *shared access* to a value that you 38 | don't own. Because you have shared access, you're generally not allowed to 39 | mutate anything. Think of `&` as putting the value out on display in a museum. 40 | `&` is great for methods that only want to observe `self`. 41 | 42 | Later we'll see that the rule about mutation can be bypassed in certain cases. 43 | This is why shared references aren't called *immutable* references. Really, 44 | mutable references could be called *unique* references, but we've found that 45 | relating ownership to mutability gives the right intuition 99% of the time. 46 | -------------------------------------------------------------------------------- /text/first-final.md: -------------------------------------------------------------------------------- 1 | % The Final Code 2 | 3 | Alright, 6000 words later, here's all the code we managed to actually write: 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 | Geez. 80 lines, and half of it was tests! Well, I did say this first one was 93 | going to take a while! 94 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 | assert_eq!(iter.next(), None); 69 | } 70 | ``` 71 | 72 | ```text 73 | > cargo test 74 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 75 | Running target/debug/lists-5c71138492ad4b4a 76 | 77 | running 4 tests 78 | test first::test::basics ... ok 79 | test second::test::basics ... ok 80 | test second::test::into_iter ... ok 81 | test second::test::peek ... ok 82 | 83 | test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured 84 | 85 | Doc-tests lists 86 | 87 | running 0 tests 88 | 89 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 90 | ``` 91 | 92 | Nice! 93 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 function is called `try_unwrap`. 42 | 43 | Rc actually lets you do this... 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: 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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/first-drop.md: -------------------------------------------------------------------------------- 1 | % Drop 2 | 3 | We can make a stack, push on to, pop off it, and we've even tested that it all 4 | works right! 5 | 6 | Do we need to worry about cleaning up our list? Technically, no, not at all! 7 | Like C++, Rust uses destructors to automatically clean up resources when they're 8 | done with. A type has a destructor if it implements a *trait* called Drop. 9 | Traits are Rust's fancy term for interfaces. The Drop trait has the following 10 | interface: 11 | 12 | ``` 13 | pub trait Drop { 14 | fn drop(&mut self); 15 | } 16 | ``` 17 | 18 | Basically, "when you go out of scope, I'll give you a second to clean up your 19 | affairs". 20 | 21 | You don't actually need to implement Drop if you contain types that implement 22 | Drop, and all you'd want to do is call *their* destructors. In the case of 23 | List, all it would want to do is drop its head, which in turn would *maybe* 24 | try to drop a `Box`. All that's handled for us automatically... with one 25 | hitch. 26 | 27 | The automatic handling is going to be bad. 28 | 29 | Let's consider a simple list: 30 | 31 | 32 | ```text 33 | list -> A -> B -> C 34 | ``` 35 | 36 | When `list` gets dropped, it will try to drop A, which will try to drop B, 37 | which will try to drop C. Some of you might rightly be getting nervous. This is 38 | recursive code, and recursive code can blow the stack! 39 | 40 | Some of you might be thinking "this is clearly tail recursive, and any decent 41 | language would ensure that such code wouldn't blow the stack". This is, in fact, 42 | incorrect! To see why, let's try to write what the compiler has to do, by 43 | manually implementing Drop for our List as the compiler would: 44 | 45 | 46 | ```rust 47 | impl Drop for List { 48 | fn drop(&mut self) { 49 | // NOTE: you can't actually explicitly call `drop` in real Rust code; 50 | // we're pretending to be the compiler! 51 | self.head.drop(); // tail recursive - good! 52 | } 53 | } 54 | 55 | impl Drop for Link { 56 | fn drop(&mut self) { 57 | match *self { 58 | Link::Empty => {} // Done! 59 | Link::More(ref mut boxed_node) => { 60 | boxed_node.drop(); // tail recursive - good! 61 | } 62 | } 63 | } 64 | } 65 | 66 | impl Drop for Box { 67 | fn drop(&mut self) { 68 | self.ptr.drop(); // uh oh, not tail recursive! 69 | deallocate(self.ptr); 70 | } 71 | } 72 | 73 | impl Drop for Node { 74 | fn drop(&mut self) { 75 | self.next.drop(); 76 | } 77 | } 78 | ``` 79 | 80 | We *can't* drop the contents of the Box *after* deallocating, so there's no 81 | way to drop in a tail-recursive manner! Instead we're going to have to manually 82 | write an iterative drop for `List` that hoists nodes out of their boxes. 83 | 84 | 85 | ```rust 86 | impl Drop for List { 87 | fn drop(&mut self) { 88 | let mut cur_link = mem::replace(&mut self.head, Link::Empty); 89 | // `while let` == "do this thing until this pattern doesn't match" 90 | while let Link::More(mut boxed_node) = cur_link { 91 | cur_link = mem::replace(&mut boxed_node.next, Link::Empty); 92 | // boxed_node goes out of scope and gets dropped here; 93 | // but its Node's `next` field has been set to Link::Empty 94 | // so no unbounded recursion occurs. 95 | } 96 | } 97 | } 98 | ``` 99 | 100 | ```text 101 | > cargo test 102 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 103 | Running target/debug/lists-5c71138492ad4b4a 104 | 105 | running 1 test 106 | test first::test::basics ... ok 107 | 108 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured 109 | 110 | Doc-tests lists 111 | 112 | running 0 tests 113 | 114 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 115 | ``` 116 | 117 | Great! 118 | -------------------------------------------------------------------------------- /text/first-push.md: -------------------------------------------------------------------------------- 1 | % Push 2 | 3 | So let's write pushing a value onto a list. `push` *mutates* the list, 4 | so we'll want to take `&mut self`. We also need to take an i32 to push: 5 | 6 | ```rust,ignore 7 | impl List { 8 | pub fn push(&mut self, elem: i32) { 9 | // TODO 10 | } 11 | } 12 | ``` 13 | 14 | First things first, we need to make a node to store our element in: 15 | 16 | ```rust,ignore 17 | pub fn push(&mut self, elem: i32) { 18 | let new_node = Node { 19 | elem: elem, 20 | next: ????? 21 | }; 22 | } 23 | ``` 24 | 25 | What goes `next`? Well, the entire old list! Can we... just do that? 26 | 27 | ```rust 28 | impl List { 29 | pub fn push(&mut self, elem: i32) { 30 | let new_node = Node { 31 | elem: elem, 32 | next: self.head, 33 | }; 34 | } 35 | } 36 | ``` 37 | 38 | ```text 39 | > cargo build 40 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 41 | src/first.rs:19:10: 19:14 error: cannot move out of borrowed content 42 | src/first.rs:19 next: self.head, 43 | ^~~~ 44 | error: aborting due to previous error 45 | Could not compile `lists`. 46 | ``` 47 | 48 | Nooooope. Rust is telling us the right thing, but it's certainly not obvious 49 | what exactly it means, or what to do about it: 50 | 51 | > cannot move out of borrowed content 52 | 53 | We're trying to move the `self.head` field out to `next`, but Rust doesn't want 54 | us doing that. This would leave `self` only partially initialized when we end 55 | the borrow and "give it back" to its rightful owner. As we said before, that's 56 | the *one* thing you can't do with an `&mut`: It would be super rude, 57 | and Rust is very polite (it would also be incredibly dangerous, but surely 58 | *that* isn't why it cares). 59 | 60 | What if we put something back? Namely, the node that we're creating: 61 | 62 | 63 | ```rust 64 | pub fn push(&mut self, elem: i32) { 65 | let new_node = Box::new(Node { 66 | elem: elem, 67 | next: self.head, 68 | }); 69 | 70 | self.head = Link::More(new_node); 71 | } 72 | ``` 73 | 74 | ```text 75 | > cargo build 76 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 77 | src/first.rs:19:10: 19:14 error: cannot move out of borrowed content 78 | src/first.rs:19 next: self.head, 79 | ^~~~ 80 | error: aborting due to previous error 81 | Could not compile `lists`. 82 | ``` 83 | 84 | No dice. In principle, this is something Rust could actually accept, but it 85 | won't (for various reasons -- the most serious being exception safety). We need 86 | some way to get the head without Rust noticing that it's gone. For advice, we 87 | turn to infamous Rust Hacker Indiana Jones: 88 | 89 | ![Indy Prepares to mem::replace](../indy.gif) 90 | 91 | Ah yes, Indy suggests the `mem::replace` maneuver. This incredibly useful 92 | function lets us steal a value out of a borrow by *replacing* it with another 93 | value. Let's just pull in `std::mem` at the top of the file, so that `mem` is in 94 | local scope: 95 | 96 | ```rust 97 | use std::mem; 98 | ``` 99 | 100 | and use it appropriately: 101 | 102 | ```rust 103 | pub fn push(&mut self, elem: i32) { 104 | let new_node = Box::new(Node { 105 | elem: elem, 106 | next: mem::replace(&mut self.head, Link::Empty), 107 | }); 108 | 109 | self.head = Link::More(new_node); 110 | } 111 | ``` 112 | 113 | Here we `replace` self.head temporarily with Link::Empty before replacing it 114 | with the new head of the list. I'm not gonna lie: this is a pretty unfortunate 115 | thing to have to do. Sadly, we must (for now). 116 | 117 | But hey, that's `push` all done! Probably. We should probably test it, honestly. 118 | Right now the easiest way to do that is probably to write `pop`, and make sure 119 | that it produces the right results. 120 | -------------------------------------------------------------------------------- /text/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. 3 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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /lists/src/silly1.rs: -------------------------------------------------------------------------------- 1 | pub struct List { 2 | left: Stack, 3 | right: Stack, 4 | } 5 | 6 | impl List { 7 | pub fn new() -> Self { 8 | List { left: Stack::new(), right: Stack::new() } 9 | } 10 | 11 | pub fn push_left(&mut self, elem: T) { self.left.push(elem) } 12 | pub fn push_right(&mut self, elem: T) { self.right.push(elem) } 13 | pub fn pop_left(&mut self) -> Option { self.left.pop() } 14 | pub fn pop_right(&mut self) -> Option { self.right.pop() } 15 | pub fn peek_left(&self) -> Option<&T> { self.left.peek() } 16 | pub fn peek_right(&self) -> Option<&T> { self.right.peek() } 17 | pub fn peek_left_mut(&mut self) -> Option<&mut T> { self.left.peek_mut() } 18 | pub fn peek_right_mut(&mut self) -> Option<&mut T> { self.right.peek_mut() } 19 | 20 | pub fn go_left(&mut self) -> bool { 21 | self.left.pop_node().map(|node| { 22 | self.right.push_node(node); 23 | }).is_some() 24 | } 25 | 26 | pub fn go_right(&mut self) -> bool { 27 | self.right.pop_node().map(|node| { 28 | self.left.push_node(node); 29 | }).is_some() 30 | } 31 | } 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | pub struct Stack { 41 | head: Link, 42 | } 43 | 44 | type Link = Option>>; 45 | 46 | struct Node { 47 | elem: T, 48 | next: Link, 49 | } 50 | 51 | impl Stack { 52 | pub fn new() -> Self { 53 | Stack { head: None } 54 | } 55 | 56 | pub fn push(&mut self, elem: T) { 57 | let new_node = Box::new(Node { 58 | elem: elem, 59 | next: None, 60 | }); 61 | 62 | self.push_node(new_node); 63 | } 64 | 65 | fn push_node(&mut self, mut node: Box>) { 66 | node.next = self.head.take(); 67 | self.head = Some(node); 68 | } 69 | 70 | pub fn pop(&mut self) -> Option { 71 | self.pop_node().map(|node| { 72 | node.elem 73 | }) 74 | } 75 | 76 | fn pop_node(&mut self) -> Option>> { 77 | self.head.take().map(|mut node| { 78 | self.head = node.next.take(); 79 | node 80 | }) 81 | } 82 | 83 | pub fn peek(&self) -> Option<&T> { 84 | self.head.as_ref().map(|node| { 85 | &node.elem 86 | }) 87 | } 88 | 89 | pub fn peek_mut(&mut self) -> Option<&mut T> { 90 | self.head.as_mut().map(|node| { 91 | &mut node.elem 92 | }) 93 | } 94 | } 95 | 96 | impl Drop for Stack { 97 | fn drop(&mut self) { 98 | let mut cur_link = self.head.take(); 99 | while let Some(mut boxed_node) = cur_link { 100 | cur_link = boxed_node.next.take(); 101 | } 102 | } 103 | } 104 | 105 | 106 | 107 | 108 | #[cfg(test)] 109 | mod test { 110 | use super::List; 111 | 112 | #[test] 113 | fn walk_aboot() { 114 | let mut list = List::new(); // [_] 115 | 116 | list.push_left(0); // [0,_] 117 | list.push_right(1); // [0, _, 1] 118 | assert_eq!(list.peek_left(), Some(&0)); 119 | assert_eq!(list.peek_right(), Some(&1)); 120 | 121 | list.push_left(2); // [0, 2, _, 1] 122 | list.push_left(3); // [0, 2, 3, _, 1] 123 | list.push_right(4); // [0, 2, 3, _, 4, 1] 124 | 125 | while list.go_left() {} // [_, 0, 2, 3, 4, 1] 126 | 127 | assert_eq!(list.pop_left(), None); 128 | assert_eq!(list.pop_right(), Some(0)); // [_, 2, 3, 4, 1] 129 | assert_eq!(list.pop_right(), Some(2)); // [_, 3, 4, 1] 130 | 131 | list.push_left(5); // [5, _, 3, 4, 1] 132 | assert_eq!(list.pop_right(), Some(3)); // [5, _, 4, 1] 133 | assert_eq!(list.pop_left(), Some(5)); // [_, 4, 1] 134 | assert_eq!(list.pop_right(), Some(4)); // [_, 1] 135 | assert_eq!(list.pop_right(), Some(1)); // [_] 136 | 137 | assert_eq!(list.pop_right(), None); 138 | assert_eq!(list.pop_left(), None); 139 | 140 | } 141 | } 142 | 143 | 144 | -------------------------------------------------------------------------------- /text/first-test.md: -------------------------------------------------------------------------------- 1 | % Testing 2 | 3 | Alright, so we've got `push` and `pop` written, now we can actually test out 4 | our stack! Rust and cargo support testing as a first-class feature, so this 5 | will be super easy. All we have to do is write a function, and annotate it with 6 | `#[test]`. 7 | 8 | Generally, we try to keep our tests next to the code that its testing in the 9 | Rust community. However we usually make a new namespace for the tests, to 10 | avoid conflicting with the "real" code. Just as we used `mod` to specify that 11 | `first.rs` should be included in `lib.rs`, we can use `mod` to basically 12 | create a whole new file *inline*: 13 | 14 | 15 | ```rust 16 | // in first.rs 17 | 18 | mod test { 19 | #[test] 20 | fn basics() { 21 | // TODO 22 | } 23 | } 24 | ``` 25 | 26 | And we invoke it with `cargo test`. 27 | 28 | ```text 29 | > cargo test 30 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 31 | Running target/debug/lists-5c71138492ad4b4a 32 | 33 | running 1 test 34 | test first::test::basics ... ok 35 | 36 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured 37 | 38 | Doc-tests lists 39 | 40 | running 0 tests 41 | 42 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 43 | ``` 44 | 45 | Yay our do-nothing test passed! Let's make it not-do-nothing. We'll do that 46 | with the `assert_eq!` macro. This isn't some special testing magic. All it 47 | does is compare the two things you give it, and panic the program if they don't 48 | match. Yep, you indicate failure to the test harness by freaking out! 49 | 50 | ```rust 51 | mod test { 52 | #[test] 53 | fn basics() { 54 | let mut list = List::new(); 55 | 56 | // Check empty list behaves right 57 | assert_eq!(list.pop(), None); 58 | 59 | // Populate list 60 | list.push(1); 61 | list.push(2); 62 | list.push(3); 63 | 64 | // Check normal removal 65 | assert_eq!(list.pop(), Some(3)); 66 | assert_eq!(list.pop(), Some(2)); 67 | 68 | // Push some more just to make sure nothing's corrupted 69 | list.push(4); 70 | list.push(5); 71 | 72 | // Check normal removal 73 | assert_eq!(list.pop(), Some(5)); 74 | assert_eq!(list.pop(), Some(4)); 75 | 76 | // Check exhaustion 77 | assert_eq!(list.pop(), Some(1)); 78 | assert_eq!(list.pop(), None); 79 | } 80 | } 81 | ``` 82 | 83 | ```text 84 | > cargo test 85 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 86 | src/first.rs:47:24: 47:33 error: failed to resolve. Use of undeclared type or module `List` [E0433] 87 | src/first.rs:47 let mut list = List::new(); 88 | ^~~~~~~~~ 89 | src/first.rs:47:24: 47:33 error: unresolved name `List::new` [E0425] 90 | src/first.rs:47 let mut list = List::new(); 91 | ^~~~~~~~~ 92 | error: aborting due to 2 previous errors 93 | ``` 94 | 95 | Oops! Because we made a new module, we need to pull in List explicitly to use 96 | it. 97 | 98 | ```rust 99 | mod test { 100 | use super::List; 101 | // everything else the same 102 | } 103 | ``` 104 | 105 | ```text 106 | > cargo test 107 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 108 | src/first.rs:45:9: 45:20 warning: unused import, #[warn(unused_imports)] on by default 109 | src/first.rs:45 use super::List; 110 | ^~~~~~~~~~~ 111 | Running target/debug/lists-5c71138492ad4b4a 112 | 113 | running 1 test 114 | test first::test::basics ... ok 115 | 116 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured 117 | 118 | Doc-tests lists 119 | 120 | running 0 tests 121 | 122 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 123 | ``` 124 | 125 | Yay! 126 | 127 | What's up with that warning though...? We clearly use List in our test! 128 | 129 | ...but only when testing! To appease the compiler (and to be friendly to our 130 | consumers), we should indicate that the whole `test` module should only be 131 | compiled if we're running tests. 132 | 133 | 134 | ``` 135 | #[cfg(test)] 136 | mod test { 137 | use super::List; 138 | // everything else the same 139 | } 140 | ``` 141 | 142 | And that's everything for testing! 143 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /lists/src/second.rs: -------------------------------------------------------------------------------- 1 | pub struct List { 2 | head: Link, 3 | } 4 | 5 | type Link = Option>>; 6 | 7 | struct Node { 8 | elem: T, 9 | next: Link, 10 | } 11 | 12 | pub struct IntoIter(List); 13 | 14 | pub struct Iter<'a, T:'a> { 15 | next: Option<&'a Node>, 16 | } 17 | 18 | pub struct IterMut<'a, T: 'a> { 19 | next: Option<&'a mut Node>, 20 | } 21 | 22 | 23 | 24 | 25 | impl List { 26 | pub fn new() -> Self { 27 | List { head: None } 28 | } 29 | 30 | pub fn push(&mut self, elem: T) { 31 | let new_node = Box::new(Node { 32 | elem: elem, 33 | next: self.head.take(), 34 | }); 35 | 36 | self.head = Some(new_node); 37 | } 38 | 39 | pub fn pop(&mut self) -> Option { 40 | self.head.take().map(|node| { 41 | let node = *node; 42 | self.head = node.next; 43 | node.elem 44 | }) 45 | } 46 | 47 | pub fn peek(&self) -> Option<&T> { 48 | self.head.as_ref().map(|node| { 49 | &node.elem 50 | }) 51 | } 52 | 53 | pub fn peek_mut(&mut self) -> Option<&mut T> { 54 | self.head.as_mut().map(|node| { 55 | &mut node.elem 56 | }) 57 | } 58 | 59 | pub fn into_iter(self) -> IntoIter { 60 | IntoIter(self) 61 | } 62 | 63 | pub fn iter(&self) -> Iter { 64 | Iter { next: self.head.as_ref().map(|node| &**node) } 65 | } 66 | 67 | pub fn iter_mut(&mut self) -> IterMut { 68 | IterMut { next: self.head.as_mut().map(|node| &mut **node) } 69 | } 70 | } 71 | 72 | impl Drop for List { 73 | fn drop(&mut self) { 74 | let mut cur_link = self.head.take(); 75 | while let Some(mut boxed_node) = cur_link { 76 | cur_link = boxed_node.next.take(); 77 | } 78 | } 79 | } 80 | 81 | impl Iterator for IntoIter { 82 | type Item = T; 83 | fn next(&mut self) -> Option { 84 | self.0.pop() 85 | } 86 | } 87 | 88 | impl<'a, T> Iterator for Iter<'a, T> { 89 | type Item = &'a T; 90 | 91 | fn next(&mut self) -> Option { 92 | self.next.map(|node| { 93 | self.next = node.next.as_ref().map(|node| &**node); 94 | &node.elem 95 | }) 96 | } 97 | } 98 | 99 | impl<'a, T> Iterator for IterMut<'a, T> { 100 | type Item = &'a mut T; 101 | 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 | 111 | 112 | 113 | 114 | #[cfg(test)] 115 | mod test { 116 | use super::List; 117 | 118 | #[test] 119 | fn basics() { 120 | let mut list = List::new(); 121 | 122 | // Check empty list behaves right 123 | assert_eq!(list.pop(), None); 124 | 125 | // Populate list 126 | list.push(1); 127 | list.push(2); 128 | list.push(3); 129 | 130 | // Check normal removal 131 | assert_eq!(list.pop(), Some(3)); 132 | assert_eq!(list.pop(), Some(2)); 133 | 134 | // Push some more just to make sure nothing's corrupted 135 | list.push(4); 136 | list.push(5); 137 | 138 | // Check normal removal 139 | assert_eq!(list.pop(), Some(5)); 140 | assert_eq!(list.pop(), Some(4)); 141 | 142 | // Check exhaustion 143 | assert_eq!(list.pop(), Some(1)); 144 | assert_eq!(list.pop(), None); 145 | } 146 | 147 | #[test] 148 | fn peek() { 149 | let mut list = List::new(); 150 | assert_eq!(list.peek(), None); 151 | assert_eq!(list.peek_mut(), None); 152 | list.push(1); list.push(2); list.push(3); 153 | 154 | assert_eq!(list.peek(), Some(&3)); 155 | assert_eq!(list.peek_mut(), Some(&mut 3)); 156 | } 157 | 158 | #[test] 159 | fn into_iter() { 160 | let mut list = List::new(); 161 | list.push(1); list.push(2); list.push(3); 162 | 163 | let mut iter = list.into_iter(); 164 | assert_eq!(iter.next(), Some(3)); 165 | assert_eq!(iter.next(), Some(2)); 166 | assert_eq!(iter.next(), Some(1)); 167 | } 168 | 169 | #[test] 170 | fn iter() { 171 | let mut list = List::new(); 172 | list.push(1); list.push(2); list.push(3); 173 | 174 | let mut iter = list.iter(); 175 | assert_eq!(iter.next(), Some(&3)); 176 | assert_eq!(iter.next(), Some(&2)); 177 | assert_eq!(iter.next(), Some(&1)); 178 | } 179 | 180 | #[test] 181 | fn iter_mut() { 182 | let mut list = List::new(); 183 | list.push(1); list.push(2); list.push(3); 184 | 185 | let mut iter = list.iter_mut(); 186 | assert_eq!(iter.next(), Some(&mut 3)); 187 | assert_eq!(iter.next(), Some(&mut 2)); 188 | assert_eq!(iter.next(), Some(&mut 1)); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /text/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_back(), 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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /lists/src/fifth.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | pub struct List { 4 | head: Link, 5 | tail: *mut Node, 6 | } 7 | 8 | type Link = Option>>; 9 | 10 | struct Node { 11 | elem: T, 12 | next: Link, 13 | } 14 | 15 | pub struct IntoIter(List); 16 | 17 | pub struct Iter<'a, T:'a> { 18 | next: Option<&'a Node>, 19 | } 20 | 21 | pub struct IterMut<'a, T: 'a> { 22 | next: Option<&'a mut Node>, 23 | } 24 | 25 | 26 | 27 | 28 | 29 | impl List { 30 | pub fn new() -> Self { 31 | List { head: None, tail: ptr::null_mut() } 32 | } 33 | 34 | pub fn push(&mut self, elem: T) { 35 | let mut new_tail = Box::new(Node { 36 | elem: elem, 37 | next: None, 38 | }); 39 | 40 | let raw_tail: *mut _ = &mut *new_tail; 41 | 42 | if !self.tail.is_null() { 43 | unsafe { 44 | (*self.tail).next = Some(new_tail); 45 | } 46 | } else { 47 | self.head = Some(new_tail); 48 | } 49 | 50 | self.tail = raw_tail; 51 | } 52 | 53 | pub fn pop(&mut self) -> Option { 54 | self.head.take().map(|head| { 55 | let head = *head; 56 | self.head = head.next; 57 | 58 | if self.head.is_none() { 59 | self.tail = ptr::null_mut(); 60 | } 61 | 62 | head.elem 63 | }) 64 | } 65 | 66 | pub fn peek(&self) -> Option<&T> { 67 | self.head.as_ref().map(|node| { 68 | &node.elem 69 | }) 70 | } 71 | 72 | pub fn peek_mut(&mut self) -> Option<&mut T> { 73 | self.head.as_mut().map(|node| { 74 | &mut node.elem 75 | }) 76 | } 77 | 78 | pub fn into_iter(self) -> IntoIter { 79 | IntoIter(self) 80 | } 81 | 82 | pub fn iter(&self) -> Iter { 83 | Iter { next: self.head.as_ref().map(|node| &**node) } 84 | } 85 | 86 | pub fn iter_mut(&mut self) -> IterMut { 87 | IterMut { next: self.head.as_mut().map(|node| &mut **node) } 88 | } 89 | } 90 | 91 | 92 | impl Drop for List { 93 | fn drop(&mut self) { 94 | let mut cur_link = self.head.take(); 95 | while let Some(mut boxed_node) = cur_link { 96 | cur_link = boxed_node.next.take(); 97 | } 98 | } 99 | } 100 | 101 | 102 | impl Iterator for IntoIter { 103 | type Item = T; 104 | fn next(&mut self) -> Option { 105 | self.0.pop() 106 | } 107 | } 108 | 109 | impl<'a, T> Iterator for Iter<'a, T> { 110 | type Item = &'a T; 111 | 112 | fn next(&mut self) -> Option { 113 | self.next.map(|node| { 114 | self.next = node.next.as_ref().map(|node| &**node); 115 | &node.elem 116 | }) 117 | } 118 | } 119 | 120 | impl<'a, T> Iterator for IterMut<'a, T> { 121 | type Item = &'a mut T; 122 | 123 | fn next(&mut self) -> Option { 124 | self.next.take().map(|node| { 125 | self.next = node.next.as_mut().map(|node| &mut **node); 126 | &mut node.elem 127 | }) 128 | } 129 | } 130 | 131 | 132 | 133 | #[cfg(test)] 134 | mod test { 135 | use super::List; 136 | #[test] 137 | fn basics() { 138 | let mut list = List::new(); 139 | 140 | // Check empty list behaves right 141 | assert_eq!(list.pop(), None); 142 | 143 | // Populate list 144 | list.push(1); 145 | list.push(2); 146 | list.push(3); 147 | 148 | // Check normal removal 149 | assert_eq!(list.pop(), Some(1)); 150 | assert_eq!(list.pop(), Some(2)); 151 | 152 | // Push some more just to make sure nothing's corrupted 153 | list.push(4); 154 | list.push(5); 155 | 156 | // Check normal removal 157 | assert_eq!(list.pop(), Some(3)); 158 | assert_eq!(list.pop(), Some(4)); 159 | 160 | // Check exhaustion 161 | assert_eq!(list.pop(), Some(5)); 162 | assert_eq!(list.pop(), None); 163 | 164 | // Check the exhaustion case fixed the pointer right 165 | list.push(6); 166 | list.push(7); 167 | 168 | // Check normal removal 169 | assert_eq!(list.pop(), Some(6)); 170 | assert_eq!(list.pop(), Some(7)); 171 | assert_eq!(list.pop(), None); 172 | } 173 | 174 | #[test] 175 | fn into_iter() { 176 | let mut list = List::new(); 177 | list.push(1); list.push(2); list.push(3); 178 | 179 | let mut iter = list.into_iter(); 180 | assert_eq!(iter.next(), Some(1)); 181 | assert_eq!(iter.next(), Some(2)); 182 | assert_eq!(iter.next(), Some(3)); 183 | assert_eq!(iter.next(), None); 184 | } 185 | 186 | #[test] 187 | fn iter() { 188 | let mut list = List::new(); 189 | list.push(1); list.push(2); list.push(3); 190 | 191 | let mut iter = list.iter(); 192 | assert_eq!(iter.next(), Some(&1)); 193 | assert_eq!(iter.next(), Some(&2)); 194 | assert_eq!(iter.next(), Some(&3)); 195 | assert_eq!(iter.next(), None); 196 | } 197 | 198 | #[test] 199 | fn iter_mut() { 200 | let mut list = List::new(); 201 | list.push(1); list.push(2); list.push(3); 202 | 203 | let mut iter = list.iter_mut(); 204 | assert_eq!(iter.next(), Some(&mut 1)); 205 | assert_eq!(iter.next(), Some(&mut 2)); 206 | assert_eq!(iter.next(), Some(&mut 3)); 207 | assert_eq!(iter.next(), None); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /lists/src/fourth.rs: -------------------------------------------------------------------------------- 1 | use std::cell::{Ref, RefMut, RefCell}; 2 | use std::rc::Rc; 3 | 4 | pub struct List { 5 | head: Link, 6 | tail: Link, 7 | } 8 | 9 | type Link = Option>>>; 10 | 11 | struct Node { 12 | elem: T, 13 | next: Link, 14 | prev: Link, 15 | } 16 | 17 | pub struct IntoIter(List); 18 | 19 | impl Node { 20 | fn new(elem: T) -> Rc> { 21 | Rc::new(RefCell::new(Node { 22 | elem: elem, 23 | prev: None, 24 | next: None, 25 | })) 26 | } 27 | } 28 | 29 | impl List { 30 | pub fn new() -> Self { 31 | List { head: None, tail: None } 32 | } 33 | 34 | pub fn push_front(&mut self, elem: T) { 35 | let new_head = Node::new(elem); 36 | match self.head.take() { 37 | Some(old_head) => { 38 | old_head.borrow_mut().prev = Some(new_head.clone()); 39 | new_head.borrow_mut().next = Some(old_head); 40 | self.head = Some(new_head); 41 | } 42 | None => { 43 | self.tail = Some(new_head.clone()); 44 | self.head = Some(new_head); 45 | } 46 | } 47 | } 48 | 49 | pub fn push_back(&mut self, elem: T) { 50 | let new_tail = Node::new(elem); 51 | match self.tail.take() { 52 | Some(old_tail) => { 53 | old_tail.borrow_mut().next = Some(new_tail.clone()); 54 | new_tail.borrow_mut().prev = Some(old_tail); 55 | self.tail = Some(new_tail); 56 | } 57 | None => { 58 | self.head = Some(new_tail.clone()); 59 | self.tail = Some(new_tail); 60 | } 61 | } 62 | } 63 | 64 | pub fn pop_front(&mut self) -> Option { 65 | self.head.take().map(|old_head| { 66 | match old_head.borrow_mut().next.take() { 67 | Some(new_head) => { 68 | new_head.borrow_mut().prev.take(); 69 | self.head = Some(new_head); 70 | } 71 | None => { 72 | self.tail.take(); 73 | } 74 | } 75 | Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem 76 | }) 77 | } 78 | 79 | pub fn pop_back(&mut self) -> Option { 80 | self.tail.take().map(|old_tail| { 81 | match old_tail.borrow_mut().prev.take() { 82 | Some(new_tail) => { 83 | new_tail.borrow_mut().next.take(); 84 | self.tail = Some(new_tail); 85 | } 86 | None => { 87 | self.head.take(); 88 | } 89 | } 90 | Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem 91 | }) 92 | } 93 | 94 | pub fn peek_front(&self) -> Option> { 95 | self.head.as_ref().map(|node| { 96 | Ref::map(node.borrow(), |node| &node.elem) 97 | }) 98 | } 99 | 100 | pub fn peek_front_mut(&mut self) -> Option> { 101 | self.head.as_ref().map(|node| { 102 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 103 | }) 104 | } 105 | 106 | pub fn peek_back(&self) -> Option> { 107 | self.tail.as_ref().map(|node| { 108 | Ref::map(node.borrow(), |node| &node.elem) 109 | }) 110 | } 111 | 112 | pub fn peek_back_mut(&mut self) -> Option> { 113 | self.tail.as_ref().map(|node| { 114 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 115 | }) 116 | } 117 | 118 | pub fn into_iter(self) -> IntoIter { 119 | IntoIter(self) 120 | } 121 | } 122 | 123 | impl Drop for List { 124 | fn drop(&mut self) { 125 | while self.pop_front().is_some() {} 126 | } 127 | } 128 | 129 | impl Iterator for IntoIter { 130 | type Item = T; 131 | fn next(&mut self) -> Option { 132 | self.0.pop_front() 133 | } 134 | } 135 | 136 | impl DoubleEndedIterator for IntoIter { 137 | fn next_back(&mut self) -> Option { 138 | self.0.pop_back() 139 | } 140 | } 141 | 142 | #[cfg(test)] 143 | mod test { 144 | use super::List; 145 | 146 | #[test] 147 | fn basics() { 148 | let mut list = List::new(); 149 | 150 | // Check empty list behaves right 151 | assert_eq!(list.pop_front(), None); 152 | 153 | // Populate list 154 | list.push_front(1); 155 | list.push_front(2); 156 | list.push_front(3); 157 | 158 | // Check normal removal 159 | assert_eq!(list.pop_front(), Some(3)); 160 | assert_eq!(list.pop_front(), Some(2)); 161 | 162 | // Push some more just to make sure nothing's corrupted 163 | list.push_front(4); 164 | list.push_front(5); 165 | 166 | // Check normal removal 167 | assert_eq!(list.pop_front(), Some(5)); 168 | assert_eq!(list.pop_front(), Some(4)); 169 | 170 | // Check exhaustion 171 | assert_eq!(list.pop_front(), Some(1)); 172 | assert_eq!(list.pop_front(), None); 173 | 174 | // ---- back ----- 175 | 176 | // Check empty list behaves right 177 | assert_eq!(list.pop_back(), None); 178 | 179 | // Populate list 180 | list.push_back(1); 181 | list.push_back(2); 182 | list.push_back(3); 183 | 184 | // Check normal removal 185 | assert_eq!(list.pop_back(), Some(3)); 186 | assert_eq!(list.pop_back(), Some(2)); 187 | 188 | // Push some more just to make sure nothing's corrupted 189 | list.push_back(4); 190 | list.push_back(5); 191 | 192 | // Check normal removal 193 | assert_eq!(list.pop_back(), Some(5)); 194 | assert_eq!(list.pop_back(), Some(4)); 195 | 196 | // Check exhaustion 197 | assert_eq!(list.pop_back(), Some(1)); 198 | assert_eq!(list.pop_back(), None); 199 | } 200 | 201 | #[test] 202 | fn peek() { 203 | let mut list = List::new(); 204 | assert!(list.peek_front().is_none()); 205 | assert!(list.peek_back().is_none()); 206 | assert!(list.peek_front_mut().is_none()); 207 | assert!(list.peek_back_mut().is_none()); 208 | 209 | list.push_front(1); list.push_front(2); list.push_front(3); 210 | 211 | assert_eq!(&*list.peek_front().unwrap(), &3); 212 | assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); 213 | assert_eq!(&*list.peek_back().unwrap(), &1); 214 | assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); 215 | } 216 | 217 | #[test] 218 | fn into_iter() { 219 | let mut list = List::new(); 220 | list.push_front(1); list.push_front(2); list.push_front(3); 221 | 222 | let mut iter = list.into_iter(); 223 | assert_eq!(iter.next(), Some(3)); 224 | assert_eq!(iter.next_back(), Some(1)); 225 | assert_eq!(iter.next(), Some(2)); 226 | assert_eq!(iter.next_back(), None); 227 | assert_eq!(iter.next(), None); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /text/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 returns 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.as_ref().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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 we *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. Let's 154 | have some *fun*. Our source of fun is *this beast*: 155 | 156 | ```rust 157 | map(orig: Ref<'b, T>, f: F) -> Ref<'b, U> 158 | where F: FnOnce(&T) -> &U, 159 | U: ?Sized 160 | ``` 161 | 162 | > Make a new Ref for a component of the borrowed data. 163 | 164 | Yes: just like you can map over an Option, you can map over a Ref. 165 | 166 | I'm sure someone somewhere is really excited because *monads* or whatever but 167 | I don't care about any of that. Also I don't think it's a proper monad since 168 | there's no None-like case. But I digress. 169 | 170 | It's cool and that's all that matters to me. *I need this*. 171 | 172 | ```rust 173 | pub fn peek_front(&self) -> Option> { 174 | self.head.as_ref().map(|node| { 175 | Ref::map(node.borrow(), |node| &node.elem) 176 | }) 177 | } 178 | ``` 179 | 180 | ```text 181 | > cargo build 182 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 183 | src/fourth.rs:1:22: 1:28 warning: unused import, #[warn(unused_imports)] on by default 184 | src/fourth.rs:1 use std::cell::{Ref, RefMut, RefCell}; 185 | ^~~~~~ 186 | ``` 187 | 188 | Awww yissss 189 | 190 | Let's make sure this is working by munging up the test from our stack. We need 191 | to do some munging to deal with the fact that Refs don't implement comparisons. 192 | 193 | ```rust 194 | #[test] 195 | fn peek() { 196 | let mut list = List::new(); 197 | assert!(list.peek_front().is_none()); 198 | list.push_front(1); list.push_front(2); list.push_front(3); 199 | 200 | assert_eq!(&*list.peek_front().unwrap(), &3); 201 | } 202 | ``` 203 | 204 | 205 | ``` 206 | > cargo test 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 | Running target/debug/lists-5c71138492ad4b4a 212 | 213 | running 10 tests 214 | test first::test::basics ... ok 215 | test fourth::test::basics ... ok 216 | test second::test::basics ... ok 217 | test fourth::test::peek ... ok 218 | test second::test::iter_mut ... ok 219 | test second::test::into_iter ... ok 220 | test third::test::basics ... ok 221 | test second::test::peek ... ok 222 | test second::test::iter ... ok 223 | test third::test::iter ... ok 224 | 225 | test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured 226 | 227 | Doc-tests lists 228 | 229 | running 0 tests 230 | 231 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 232 | ``` 233 | 234 | Great! 235 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /rust.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT 3 | * file at the top-level directory of this distribution and at 4 | * http://rust-lang.org/COPYRIGHT. 5 | * With elements taken from Bootstrap v3.0.2 (MIT licensed). 6 | * 7 | * Licensed under the Apache License, Version 2.0 or the MIT license 9 | * , at your 10 | * option. This file may not be copied, modified, or distributed 11 | * except according to those terms. 12 | */ 13 | @font-face { 14 | font-family: 'Fira Sans'; 15 | font-style: normal; 16 | font-weight: 400; 17 | src: local('Fira Sans'), url("FiraSans-Regular.woff") format('woff'); 18 | } 19 | @font-face { 20 | font-family: 'Fira Sans'; 21 | font-style: normal; 22 | font-weight: 500; 23 | src: local('Fira Sans Medium'), url("FiraSans-Medium.woff") format('woff'); 24 | } 25 | @font-face { 26 | font-family: 'Source Serif Pro'; 27 | font-style: normal; 28 | font-weight: 400; 29 | src: local('Source Serif Pro'), url("SourceSerifPro-Regular.woff") format('woff'); 30 | } 31 | @font-face { 32 | font-family: 'Source Serif Pro'; 33 | font-style: italic; 34 | font-weight: 400; 35 | src: url("Heuristica-Italic.woff") format('woff'); 36 | } 37 | @font-face { 38 | font-family: 'Source Serif Pro'; 39 | font-style: normal; 40 | font-weight: 700; 41 | src: local('Source Serif Pro Bold'), url("SourceSerifPro-Bold.woff") format('woff'); 42 | } 43 | @font-face { 44 | font-family: 'Source Code Pro'; 45 | font-style: normal; 46 | font-weight: 400; 47 | src: local('Source Code Pro'), url("SourceCodePro-Regular.woff") format('woff'); 48 | } 49 | 50 | *:not(body) { 51 | -webkit-box-sizing: border-box; 52 | -moz-box-sizing: border-box; 53 | box-sizing: border-box; 54 | } 55 | 56 | /* General structure */ 57 | 58 | body { 59 | background-color: white; 60 | margin: 0 auto; 61 | padding: 0 15px; 62 | font-family: "Source Serif Pro", Georgia, Times, "Times New Roman", serif; 63 | font-size: 18px; 64 | color: #333; 65 | line-height: 1.428571429; 66 | 67 | -webkit-font-feature-settings: "kern", "liga"; 68 | -moz-font-feature-settings: "kern", "liga"; 69 | font-feature-settings: "kern", "liga"; 70 | } 71 | @media (min-width: 768px) { 72 | body { 73 | max-width: 750px; 74 | } 75 | } 76 | 77 | h1, h2, h3, h4, h5, h6, nav, #versioninfo { 78 | font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 79 | } 80 | h1, h2, h3, h4, h5, h6 { 81 | color: black; 82 | font-weight: 400; 83 | line-height: 1.1; 84 | } 85 | h1, h2, h3 { 86 | margin-top: 20px; 87 | margin-bottom: 15px; 88 | } 89 | h1 { 90 | margin-bottom: 20px; 91 | } 92 | h4, h5, h6 { 93 | margin-top: 12px; 94 | margin-bottom: 10px; 95 | padding: 5px 10px; 96 | } 97 | h5, h6 { 98 | text-decoration: underline; 99 | } 100 | 101 | h1 { 102 | font-size: 28px; 103 | font-weight: 500; 104 | padding: .1em .4em; 105 | border-bottom: 2px solid #ddd; 106 | } 107 | h1.title { 108 | line-height: 1.5em; 109 | } 110 | h2 { 111 | font-size: 26px; 112 | padding: .2em .5em; 113 | border-bottom: 1px solid #ddd; 114 | } 115 | h3 { 116 | font-size: 24px; 117 | padding: .2em .7em; 118 | border-bottom: 1px solid #DDE8FC; 119 | } 120 | h4 { 121 | font-size: 22px; 122 | } 123 | h5 { 124 | font-size: 20px; 125 | } 126 | h6 { 127 | font-size: 18px; 128 | } 129 | @media (min-width: 992px) { 130 | h1 { 131 | font-size: 36px; 132 | } 133 | h2 { 134 | font-size: 30px; 135 | } 136 | h3 { 137 | font-size: 26px; 138 | } 139 | } 140 | 141 | nav { 142 | column-count: 2; 143 | -moz-column-count: 2; 144 | -webkit-column-count: 2; 145 | font-size: 15px; 146 | margin: 0 0 1em 0; 147 | } 148 | p { 149 | margin: 0 0 1em 0; 150 | } 151 | 152 | strong { 153 | font-weight: bold; 154 | } 155 | 156 | em { 157 | font-style: italic; 158 | } 159 | 160 | footer { 161 | border-top: 1px solid #ddd; 162 | font-size: 14.3px; 163 | font-style: italic; 164 | padding-top: 5px; 165 | margin-top: 3em; 166 | margin-bottom: 1em; 167 | } 168 | 169 | /* Links layout */ 170 | 171 | a { 172 | text-decoration: none; 173 | color: #428BCA; 174 | background: transparent; 175 | } 176 | a:hover, a:focus { 177 | color: #2A6496; 178 | text-decoration: underline; 179 | } 180 | a:focus { 181 | outline: thin dotted #333; 182 | outline: 5px auto -webkit-focus-ring-color; 183 | outline-offset: -2px; 184 | } 185 | a:hover, a:active { 186 | outline: 0; 187 | } 188 | 189 | h1 a:link, h1 a:visited, h2 a:link, h2 a:visited, 190 | h3 a:link, h3 a:visited, h4 a:link, h4 a:visited, 191 | h5 a:link, h5 a:visited {color: black;} 192 | h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, 193 | h5 a:hover {text-decoration: none;} 194 | 195 | /* Code */ 196 | 197 | pre, code { 198 | font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", monospace; 199 | word-wrap: break-word; 200 | } 201 | pre { 202 | border-left: 2px solid #eee; 203 | white-space: pre-wrap; 204 | padding: 14px; 205 | padding-right: 0; 206 | margin: 20px 0; 207 | font-size: 13px; 208 | word-break: break-all; 209 | } 210 | code { 211 | padding: 0 2px; 212 | color: #8D1A38; 213 | } 214 | pre code { 215 | padding: 0; 216 | font-size: inherit; 217 | color: inherit; 218 | } 219 | 220 | a > code { 221 | color: #428BCA; 222 | } 223 | 224 | /* Code highlighting */ 225 | pre.rust .kw { color: #8959A8; } 226 | pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; } 227 | pre.rust .number, pre.rust .string { color: #718C00; } 228 | pre.rust .self, pre.rust .boolval, pre.rust .prelude-val, 229 | pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; } 230 | pre.rust .comment { color: #8E908C; } 231 | pre.rust .doccomment { color: #4D4D4C; } 232 | pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } 233 | pre.rust .lifetime { color: #B76514; } 234 | 235 | /* The rest */ 236 | 237 | #versioninfo { 238 | text-align: center; 239 | margin: 0.5em; 240 | font-size: 1.1em; 241 | } 242 | @media (min-width: 992px) { 243 | #versioninfo { 244 | font-size: 0.8em; 245 | position: fixed; 246 | bottom: 0px; 247 | right: 0px; 248 | } 249 | .white-sticker { 250 | background-color: #fff; 251 | margin: 2px; 252 | padding: 0 2px; 253 | border-radius: .2em; 254 | } 255 | } 256 | #versioninfo a.hash { 257 | color: gray; 258 | font-size: 80%; 259 | } 260 | 261 | blockquote { 262 | color: #000; 263 | margin: 20px 0; 264 | padding: 15px 20px; 265 | background-color: #f2f7f9; 266 | border-top: .1em solid #e5eef2; 267 | border-bottom: .1em solid #e5eef2; 268 | } 269 | blockquote p { 270 | font-size: 17px; 271 | font-weight: 300; 272 | line-height: 1.4; 273 | } 274 | blockquote p:last-child { 275 | margin-bottom: 0; 276 | } 277 | 278 | ul, ol { 279 | padding-left: 25px; 280 | } 281 | ul ul, ol ul, ul ol, ol ol { 282 | margin-bottom: 0; 283 | } 284 | dl { 285 | margin-bottom: 20px; 286 | } 287 | dd { 288 | margin-left: 0; 289 | } 290 | 291 | nav ul { 292 | list-style-type: none; 293 | margin: 0; 294 | padding-left: 0px; 295 | } 296 | 297 | /* Only display one level of hierarchy in the TOC */ 298 | nav ul ul { 299 | display: none; 300 | } 301 | 302 | sub, 303 | sup { 304 | font-size: 75%; 305 | line-height: 0; 306 | position: relative; 307 | } 308 | 309 | hr { 310 | margin-top: 20px; 311 | margin-bottom: 20px; 312 | border: 0; 313 | border-top: 1px solid #eeeeee; 314 | } 315 | 316 | table { 317 | border-collapse: collapse; 318 | border-spacing: 0; 319 | overflow-x: auto; 320 | display: block; 321 | } 322 | 323 | table tr.odd { 324 | background: #eee; 325 | } 326 | 327 | table td, 328 | table th { 329 | border: 1px solid #ddd; 330 | padding: 5px; 331 | } 332 | 333 | /* Code snippets */ 334 | 335 | .rusttest { display: none; } 336 | pre.rust { position: relative; } 337 | .test-arrow { 338 | display: inline-block; 339 | position: absolute; 340 | top: 0; 341 | right: 10px; 342 | font-size: 150%; 343 | -webkit-transform: scaleX(-1); 344 | transform: scaleX(-1); 345 | } 346 | 347 | .unstable-feature { 348 | border: 2px solid red; 349 | padding: 5px; 350 | } 351 | 352 | @media (min-width: 1170px) { 353 | pre { 354 | font-size: 15px; 355 | } 356 | } 357 | 358 | @media print { 359 | * { 360 | text-shadow: none !important; 361 | color: #000 !important; 362 | background: transparent !important; 363 | box-shadow: none !important; 364 | } 365 | a, a:visited { 366 | text-decoration: underline; 367 | } 368 | p a[href]:after { 369 | content: " (" attr(href) ")"; 370 | } 371 | footer a[href]:after { 372 | content: ""; 373 | } 374 | a[href^="javascript:"]:after, a[href^="#"]:after { 375 | content: ""; 376 | } 377 | pre, blockquote { 378 | border: 1px solid #999; 379 | page-break-inside: avoid; 380 | } 381 | @page { 382 | margin: 2cm .5cm; 383 | } 384 | h1:not(.title), h2, h3 { 385 | border-bottom: 0px none; 386 | } 387 | p, h2, h3 { 388 | orphans: 3; 389 | widows: 3; 390 | } 391 | h2, h3 { 392 | page-break-after: avoid; 393 | } 394 | table { 395 | border-collapse: collapse !important; 396 | } 397 | table td, table th { 398 | background-color: #fff !important; 399 | } 400 | } 401 | 402 | #keyword-table-marker + table thead { display: none; } 403 | #keyword-table-marker + table td { border: none; } 404 | #keyword-table-marker + table { 405 | margin-left: 2em; 406 | margin-bottom: 1em; 407 | } 408 | -------------------------------------------------------------------------------- /text/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. 118 | 119 | Now we can use `Rc::try_unwrap`, which moves out the contents of an Rc out 120 | if its refcount is 1. 121 | 122 | ```rust 123 | Rc::try_unwrap(old_head).unwrap().into_inner().elem 124 | ``` 125 | 126 | `Rc::try_unwrap` returns a `Result>`. Results are basically a 127 | generalized `Option`, where the `None` case has data associated with it. In 128 | this case, the `Rc` you tried to unwrap. Since we don't care about the case 129 | where it fails (if we wrote our program correctly, it *has* to succeed), we 130 | just call `unwrap` on it. 131 | 132 | `unwrap` is a function on Results and Options that basically says "get the 133 | the value out, or panic the program". Panics are A Whole Thing, so I'll simplify 134 | things by just saying a panics crash your program in a controlled manner, 135 | effectively making every function instantly return. Notably, it makes sure to 136 | call all the destructors that happen to be laying around on the way out. So your 137 | memory will get freed (meh? OS does that anyway, right?) and your Connections 138 | and Files will get properly saved/closed (yay!). 139 | 140 | This is a bit of a lie, there's cases where you can stop a panic. In particular 141 | at a thread boundary if you're into that whole "parallelism" thing. That whole 142 | freeing your memory thing is less lame there, *I guess*. 143 | 144 | Anyway, let's see what compiler error we get next (let's face it, there's going 145 | to be one). 146 | 147 | ```text 148 | > cargo build 149 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 150 | 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 151 | src/fourth.rs:58 Rc::try_unwrap(old_head).unwrap().into_inner().elem 152 | ^~~~~~~~ 153 | note: in expansion of closure expansion 154 | src/fourth.rs:48:30: 59:10 note: expansion site 155 | 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` 156 | error: aborting due to previous error 157 | Could not compile `lists`. 158 | ``` 159 | 160 | UGH. This is dumb. Rust is dumb. `unwrap` on Result requires that you can 161 | debug-print the error case. `RefCell` only implements `Debug` if `T` does. 162 | `Node` doesn't implement Debug. We could just implement Debug for Node but I'm 163 | not in the mood. Let's just work around this by turning the `Result` into an 164 | `Option` with the `ok` method: 165 | 166 | ```rust 167 | Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem 168 | ``` 169 | 170 | PLEASE. 171 | 172 | ```text 173 | cargo build 174 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 175 | ``` 176 | 177 | YES. 178 | 179 | *phew* 180 | 181 | We did it. 182 | 183 | We implemented `push` and `pop`. 184 | 185 | Let's test by stealing the old `stack` basic test (because that's all that 186 | we've implemented so far): 187 | 188 | ``` 189 | #[cfg(test)] 190 | mod test { 191 | use super::List; 192 | 193 | #[test] 194 | fn basics() { 195 | let mut list = List::new(); 196 | 197 | // Check empty list behaves right 198 | assert_eq!(list.pop_front(), None); 199 | 200 | // Populate list 201 | list.push_front(1); 202 | list.push_front(2); 203 | list.push_front(3); 204 | 205 | // Check normal removal 206 | assert_eq!(list.pop_front(), Some(3)); 207 | assert_eq!(list.pop_front(), Some(2)); 208 | 209 | // Push some more just to make sure nothing's corrupted 210 | list.push_front(4); 211 | list.push_front(5); 212 | 213 | // Check normal removal 214 | assert_eq!(list.pop_front(), Some(5)); 215 | assert_eq!(list.pop_front(), Some(4)); 216 | 217 | // Check exhaustion 218 | assert_eq!(list.pop_front(), Some(1)); 219 | assert_eq!(list.pop_front(), None); 220 | } 221 | } 222 | ``` 223 | 224 | ```text 225 | cargo test 226 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 227 | Running target/debug/lists-5c71138492ad4b4a 228 | 229 | running 9 tests 230 | test first::test::basics ... ok 231 | test fourth::test::basics ... ok 232 | test second::test::iter_mut ... ok 233 | test second::test::basics ... ok 234 | test fifth::test::iter_mut ... ok 235 | test third::test::basics ... ok 236 | test second::test::iter ... ok 237 | test third::test::iter ... ok 238 | test second::test::into_iter ... ok 239 | 240 | test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured 241 | 242 | Doc-tests lists 243 | 244 | running 0 tests 245 | 246 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured 247 | ``` 248 | 249 | *Nailed it*. 250 | 251 | Now that we can properly remove things from the list, we can implement Drop. 252 | Drop is a little more conceptually interesting this time around. Where 253 | previously we bothered to implement Drop for our stacks just to avoid unbounded 254 | recursion, now we need to implement Drop to get *anything* to happen at all. 255 | 256 | `Rc` can't deal with cycles. If there's a cycle, everything will keep everything 257 | else alive. A doubly linked list, as it turns out, is just a big chain of tiny 258 | cycles! So when we drop our list, the two end nodes will have their refcounts 259 | decremented down to 1... and then nothing else will happen. Well, if our list 260 | contains exactly one node we're good to go. But ideally a list should work right 261 | if it contains multiple elements. Maybe that's just me. 262 | 263 | As we saw, removing elements was a bit painful. So the easiest thing for us to 264 | do is just `pop` until we get None: 265 | 266 | ```rust 267 | impl Drop for List { 268 | fn drop(&mut self) { 269 | while self.pop_front().is_some() {} 270 | } 271 | } 272 | ``` 273 | 274 | ```text 275 | cargo build 276 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/too-many-lists/lists) 277 | ``` 278 | 279 | (We actually could have done this with our mutable stacks, but shortcuts are for 280 | people who understand things!) 281 | 282 | We could look at implementing the `_back` versions of `push` and `pop`, but 283 | they're just copy-paste jobs which we'll defer to later in the chapter. For now 284 | let's look at more interesting things! 285 | 286 | 287 | [refcell]: https://doc.rust-lang.org/std/cell/struct.RefCell.html 288 | [multirust]: https://github.com/brson/multirust 289 | [downloads]: https://www.rust-lang.org/install.html 290 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/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 | -------------------------------------------------------------------------------- /text/first-layout.md: -------------------------------------------------------------------------------- 1 | % Basic Data Layout 2 | 3 | Alright, so what's a linked list? Well basically, it's a bunch of pieces of data 4 | on the heap (shhh Linux Kernel!) that point to each other in sequence. Linked 5 | lists are something procedural programmers shouldn't touch with a 10-foot pole, 6 | and what functional programmers use for everything. It seems fair, then, that we 7 | should ask functional programmers for the definition of a linked list. They will 8 | probably give you something like the following definition: 9 | 10 | ```haskell 11 | List a = Empty | Elem a (List a) 12 | ``` 13 | 14 | Which reads approximately as "A List is either Empty or an Element followed by a 15 | List". This is a recursive definition expressed as a *sum type*, which is a 16 | fancy name for "a type that can have different values which may be different 17 | types". Rust calls sum types `enum`s! If you're coming from a C-like language, 18 | this is exactly the enum you know and love, but on meth. So let's transcribe 19 | this functional definition into Rust! 20 | 21 | For now we'll avoid generics to keep things simple. We'll only support 22 | storing signed 32-bit integers: 23 | 24 | ```rust,ignore 25 | // in first.rs 26 | 27 | // pub says we want people outside this module to be able to use List 28 | pub enum List { 29 | Empty, 30 | Elem(i32, List), 31 | } 32 | ``` 33 | 34 | *phew*, I'm swamped. Let's just go ahead and compile that: 35 | 36 | ```text 37 | > cargo build 38 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 39 | src/first.rs:1:1: 4:2 error: illegal recursive enum type; wrap the inner value in a box to make it representable [E0072] 40 | src/first.rs:1 pub enum List { 41 | src/first.rs:2 Empty, 42 | src/first.rs:3 Elem(T, List), 43 | src/first.rs:4 } 44 | error: aborting due to previous error 45 | Could not compile `lists`. 46 | ``` 47 | 48 | Noooooooo!!!! Functional programmers tricked us! That made us do something 49 | *illegal*! This is entrapment! 50 | 51 | ... 52 | 53 | I'm ok now. Are you ok now? If we actually check out the error message (instead 54 | of getting ready to flee the country, as \*ahem\* *some* of us did), we can see 55 | that rustc is actually telling us exactly how to solve this problem: 56 | 57 | > illegal recursive enum type; wrap the inner value in a box to make it representable 58 | 59 | Alright, `box`. What's that? Let's google `rust box`... 60 | 61 | > [std::boxed::Box - Rust](https://doc.rust-lang.org/std/boxed/struct.Box.html) 62 | 63 | Lesse here... 64 | 65 | > `pub struct Box(_);` 66 | > 67 | > A pointer type for heap allocation. 68 | > See the [module-level documentation](https://doc.rust-lang.org/std/boxed/) for more. 69 | 70 | *clicks link* 71 | 72 | > `Box`, casually referred to as a 'box', provides the simplest form of heap allocation in Rust. Boxes provide ownership for this allocation, and drop their contents when they go out of scope. 73 | > 74 | > Examples 75 | > 76 | > Creating a box: 77 | > 78 | > `let x = Box::new(5);` 79 | > 80 | > Creating a recursive data structure: 81 | > 82 | ``` 83 | #[derive(Debug)] 84 | enum List { 85 | Cons(T, Box>), 86 | Nil, 87 | } 88 | ``` 89 | > 90 | ``` 91 | fn main() { 92 | let list: List = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil)))); 93 | println!("{:?}", list); 94 | } 95 | ``` 96 | > 97 | > This will print `Cons(1, Box(Cons(2, Box(Nil))))`. 98 | > 99 | > Recursive structures must be boxed, because if the definition of Cons looked like this: 100 | > 101 | > `Cons(T, List),` 102 | > 103 | > It wouldn't work. This is because the size of a List depends on how many elements are in the list, and so we don't know how much memory to allocate for a Cons. By introducing a Box, which has a defined size, we know how big Cons needs to be. 104 | 105 | Wow, uh. That is perhaps the most relevant and helpful documentation I have ever seen. Literally the first thing in the documentation is *exactly what we're trying to write, why it didn't work, and how to fix it*. Dang, yo. Docs. 106 | 107 | Ok, let's do that: 108 | 109 | ```rust 110 | pub enum List { 111 | Empty, 112 | Elem(i32, Box), 113 | } 114 | ``` 115 | 116 | ```text 117 | > cargo build 118 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 119 | ``` 120 | 121 | Hey it built! 122 | 123 | ...but this is actually a really stupid definition of a List, for a few reasons. 124 | 125 | Consider a list with two elements: 126 | 127 | ```text 128 | [] = Stack 129 | () = Heap 130 | 131 | [Elem A, ptr] -> (Elem B, ptr) -> (Empty *junk*) 132 | ``` 133 | 134 | There are two key issues: 135 | 136 | * We're allocating a node that just says "I'm not actually a Node" 137 | * One of our nodes isn't allocated at all. 138 | 139 | On the surface, these two seem to cancel each-other out. We allocate an 140 | extra node, but one of our nodes doesn't need to be allocated at all. 141 | However, consider the following potential layout for our list: 142 | 143 | ```text 144 | [ptr] -> (Elem A, ptr) -> (Elem B, *null*) 145 | ``` 146 | 147 | In this layout we now unconditionally heap allocate our nodes. The 148 | key difference is the absence of the *junk* from our first layout. What is 149 | this junk? To understand that, we'll need to look at how an enum is laid out 150 | in memory. 151 | 152 | In general, if we have an enum like: 153 | 154 | ```rust 155 | enum Foo { 156 | D1(T1), 157 | D2(T2), 158 | ... 159 | Dn(Tn), 160 | } 161 | ``` 162 | 163 | A Foo will need to store some integer to indicate which *variant* of the enum it 164 | represents (`D1`, `D2`, .. `Dn`). This is the *tag* of the enum. It will also 165 | need enough space to store the *largest* of `T1`, `T2`, .. `Tn` (plus some extra 166 | space to satisfy alignment requirements). 167 | 168 | The big takeaway here is that even though `Empty` is a single bit of 169 | information, it necessarily consumes enough space for a pointer and an element, 170 | because it has to be ready to become an `Elem` at any time. Therefore the first 171 | layout heap allocates an extra element that's just full of junk, consuming a 172 | bit more space than the second layout. 173 | 174 | One of our nodes not being allocated at all is also, perhaps surprisingly, 175 | *worse* than always allocating it. This is because it gives us a *non-uniform* 176 | node layout. This doesn't have much of an appreciable effect on pushing and 177 | popping nodes, but it does have an effect on splitting and merging lists. 178 | 179 | Consider splitting a list in both layouts: 180 | 181 | ```text 182 | layout 1: 183 | 184 | [Elem A, ptr] -> (Elem B, ptr) -> (Elem C, ptr) -> (Empty *junk*) 185 | 186 | split off C: 187 | 188 | [Elem A, ptr] -> (Elem B, ptr) -> (Empty *junk*) 189 | [Elem C, ptr] -> (Empty *junk*) 190 | ``` 191 | 192 | ```text 193 | layout 2: 194 | 195 | [ptr] -> (Elem A, ptr) -> (Elem B, ptr) -> (Elem C, *null*) 196 | 197 | split off C: 198 | 199 | [ptr] -> (Elem A, ptr) -> (Elem B, *null*) 200 | [ptr] -> (Elem C, *null*) 201 | ``` 202 | 203 | Layout 2's split involves just copying B's pointer to the stack and nulling 204 | the old value out. Layout 1 ultimately does the same thing, but also has to 205 | copy C from the heap to the stack. Merging is the same process in reverse. 206 | 207 | One of the few nice things about a linked list is that you can construct the 208 | element in the node itself, and then freely shuffle it around lists without 209 | ever moving it. You just fiddle with pointers and stuff gets "moved". Layout 1 210 | trashes this property. 211 | 212 | Alright, I'm reasonably convinced Layout 1 is bad. How do we rewrite our List? 213 | Well, we could do something like: 214 | 215 | ```rust 216 | pub enum List { 217 | Empty, 218 | ElemThenEmpty(i32), 219 | ElemThenNotEmpty(i32, Box), 220 | } 221 | ``` 222 | 223 | Hopefully this seems like an even worse idea to you. For one, this really 224 | complicates our logic. In particular, there is now a completely invalid state: 225 | `ElemThenNotEmpty(0, Box(Empty))`. It also *still* suffers from non-uniformly 226 | allocating our elements. 227 | 228 | However it does have *one* interesting property: it totally avoids allocating 229 | the Empty case, reducing the total number of heap allocations by 1. Unfortunately, 230 | in doing so it manages to waste *even more space*! This is because the previous 231 | layout took advantage of the *null pointer optimization*. 232 | 233 | We previously saw that every enum has to store a *tag* to specify which variant 234 | of the enum its bits represent. However, if we have a special kind of enum: 235 | 236 | ```rust 237 | enum Foo { 238 | A, 239 | B(ContainsANonNullPtr), 240 | } 241 | ``` 242 | 243 | the null pointer optimization kicks in, which *eliminates the space needed for 244 | the tag*. If the variant is A, the whole enum is set to all `0`'s. Otherwise, 245 | the variant is B. This works because B can never be all `0`'s, since it contains 246 | a non-zero pointer. Slick! 247 | 248 | Can you think of other enums and types that could do this kind of optimization? 249 | There's actually a lot! This is why Rust leaves enum layout totally unspecified. 250 | Sadly the null pointer optimization is the only one implemented today -- though 251 | it's pretty important! It means `&`, `&mut`, `Box`, `Rc`, `Arc`, `Vec`, and 252 | several other important types in Rust have no overhead when put in an `Option`! 253 | (We'll get to most of these in due time). 254 | 255 | So how do we avoid the extra junk, uniformly allocate, *and* get that sweet 256 | null-pointer optimization? We need to better separate out the idea of having an 257 | element from allocating another list. To do this, we have to think a little more 258 | C-like: structs! 259 | 260 | While enums let us declare a type that can contain *one* of several values, 261 | structs let us declare a type that contains *many* values at once. Let's break 262 | our List into two types: A List, and a Node. 263 | 264 | As before, a List is either Empty or has an element followed by another List. 265 | However by representing the "has an element followed by another List" case by an 266 | entirely separate type, we can hoist the Box to be in a more optimal position: 267 | 268 | ```rust 269 | struct Node { 270 | elem: i32, 271 | next: List, 272 | } 273 | 274 | pub enum List { 275 | Empty, 276 | More(Box), 277 | } 278 | ``` 279 | 280 | Let's check our priorities: 281 | 282 | * Tail of a list never allocates extra junk: check! 283 | * `enum` is in delicious null-pointer-optimized form: check! 284 | * All elements are uniformly allocated: check! 285 | 286 | Alright! We actually just constructed exactly the layout that we used to 287 | demonstrate that our first layout (as suggested by the official Rust 288 | documentation) was problematic. 289 | 290 | ```text 291 | > cargo build 292 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 293 | src/first.rs:8:11: 8:18 error: private type in exported type signature 294 | src/first.rs:8 More(Box), 295 | ^~~~~~~ 296 | error: aborting due to previous error 297 | Could not compile `lists`. 298 | ``` 299 | 300 | :( 301 | 302 | Rust is mad at us again. We marked the `List` as public (because we want people 303 | to be able to use it), but not the `Node`. The problem is that the internals of 304 | an `enum` are totally public, and we're not allowed to publicly talk about 305 | private types. We could make all of `Node` totally public, but generally in Rust 306 | we favour keeping implementation details private. Let's make `List` a struct, so 307 | that we can hide the implementation details: 308 | 309 | 310 | ```rust 311 | pub struct List { 312 | head: Link, 313 | } 314 | 315 | enum Link { 316 | Empty, 317 | More(Box), 318 | } 319 | 320 | struct Node { 321 | elem: i32, 322 | next: Link, 323 | } 324 | ``` 325 | 326 | Because `List` is a struct with a single field, its size is the same as that 327 | field. Yay zero-cost abstractions! 328 | 329 | ```text 330 | > cargo build 331 | Compiling lists v0.1.0 (file:///Users/ABeingessner/dev/lists) 332 | src/first.rs:2:2: 2:15 warning: struct field is never used: `head`, #[warn(dead_code)] on by default 333 | src/first.rs:2 head: Link, 334 | ^~~~~~~~~~~~~ 335 | src/first.rs:6:2: 6:7 warning: variant is never used: `Empty`, #[warn(dead_code)] on by default 336 | src/first.rs:6 Empty, 337 | ^~~~~ 338 | src/first.rs:7:2: 7:20 warning: variant is never used: `More`, #[warn(dead_code)] on by default 339 | src/first.rs:7 More(Box), 340 | ^~~~~~~~~~~~~~~~~~ 341 | src/first.rs:11:2: 11:9 warning: struct field is never used: `elem`, #[warn(dead_code)] on by default 342 | src/first.rs:11 elem: i32, 343 | ^~~~~~~ 344 | src/first.rs:12:2: 12:15 warning: struct field is never used: `next`, #[warn(dead_code)] on by default 345 | src/first.rs:12 next: Link, 346 | ^~~~~~~~~~~~~ 347 | ``` 348 | 349 | Alright, that compiled! Rust is pretty mad, because as far as it can tell, 350 | everything we've written is totally useless: we never use `head`, and no one who 351 | uses our library can either since it's private. Transitively, that means Link 352 | and Node are useless too. So let's solve that! Let's implement some code for our 353 | List! 354 | --------------------------------------------------------------------------------