├── .github └── workflows │ └── main.yml ├── .gitignore ├── README.md ├── book.toml ├── license-MIT ├── lists ├── .gitignore ├── Cargo.toml └── src │ ├── fifth.rs │ ├── first.rs │ ├── fourth.rs │ ├── lib.rs │ ├── second.rs │ ├── silly1.rs │ ├── silly2.rs │ └── third.rs └── src ├── README.md ├── SUMMARY.md ├── fifth-basics.md ├── fifth-extras.md ├── fifth-final.md ├── fifth-layout-basics-redux.md ├── fifth-layout.md ├── fifth-miri.md ├── fifth-stacked-borrows.md ├── fifth-testing-stacked-borrows.md ├── fifth-unsafe.md ├── fifth.md ├── first-drop.md ├── first-final.md ├── first-layout.md ├── first-new.md ├── first-ownership.md ├── first-pop.md ├── first-push.md ├── first-test.md ├── first.md ├── fourth-breaking.md ├── fourth-building.md ├── fourth-final.md ├── fourth-iteration.md ├── fourth-layout.md ├── fourth-peek.md ├── fourth-symmetry.md ├── fourth.md ├── img ├── indy.gif └── profbee.gif ├── infinity-double-single.md ├── infinity-stack-allocated.md ├── infinity.md ├── second-final.md ├── second-generic.md ├── second-into-iter.md ├── second-iter-mut.md ├── second-iter.md ├── second-option.md ├── second-peek.md ├── second.md ├── sixth-basics.md ├── sixth-combinatorics.md ├── sixth-cursors-impl.md ├── sixth-cursors-intro.md ├── sixth-cursors-testing.md ├── sixth-final.md ├── sixth-layout.md ├── sixth-panics.md ├── sixth-random-bits.md ├── sixth-send-sync.md ├── sixth-testing.md ├── sixth-variance.md ├── sixth.md ├── third-arc.md ├── third-basics.md ├── third-drop.md ├── third-final.md ├── third-layout.md └── third.md /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | - '!gh-pages' 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Install mdBook 17 | run: | 18 | pkgver=0.4.6 19 | url="https://github.com/rust-lang/mdBook/releases/download/v${pkgver}/mdbook-v${pkgver}-x86_64-unknown-linux-gnu.tar.gz" 20 | mkdir "$HOME/bin" 21 | curl -sSL "$url" | tar -xzf - -C "$HOME/bin" 22 | echo "$HOME/bin" >> $GITHUB_PATH 23 | 24 | - name: Run mdBook 25 | run: | 26 | mdbook build 27 | 28 | - name: Deploy docs to gh-pages branch 29 | if: github.event_name != 'pull_request' && github.event.ref == 'refs/heads/master' 30 | working-directory: book 31 | run: | 32 | # Bypassing Jekyll on GitHub Pages 33 | touch .nojekyll 34 | REV=$(git rev-parse --short HEAD) 35 | git init 36 | git config user.name "Actions" 37 | git config user.email "" 38 | git add -A . 39 | git commit -qm "Documentation for ${{github.repository}}@${REV}" 40 | git remote add upstream https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} 41 | git push -q upstream HEAD:gh-pages --force 42 | shell: bash 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn Rust by writing Entirely Too Many Linked Lists 2 | [![Build Status](https://travis-ci.org/rust-unofficial/too-many-lists.svg?branch=master)](https://travis-ci.org/rust-unofficial/too-many-lists) 3 | 4 | Read the pretty version at https://rust-unofficial.github.io/too-many-lists/. 5 | 6 | # Building 7 | 8 | Building requires mdbook, which can be installed from crates.io: 9 | 10 | ```sh 11 | cargo install mdbook 12 | ``` 13 | 14 | Assuming you've placed the install directory `~/.cargo/bin` into your system PATH, then run from the root of your local copy: 15 | 16 | ```sh 17 | mdbook build 18 | ``` 19 | 20 | --- 21 | 22 | If you'd prefer, this project can also be built with 23 | [GitBook](https://github.com/GitbookIO/gitbook), although GitBook 24 | is not officially supported and compatibility is therefore 25 | uncertain and incidental. 26 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Learning Rust With Entirely Too Many Linked Lists" 3 | author = "Aria Desires" 4 | description = "Learning Rust With Entirely Too Many Linked Lists" 5 | 6 | [build] 7 | build-dir = "book" 8 | create-missing = false 9 | 10 | [output.html] 11 | git-repository-url = "https://github.com/rust-unofficial/too-many-lists" 12 | 13 | [output.html.search] 14 | limit-results = 30 15 | -------------------------------------------------------------------------------- /license-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Aria Desires 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 | -------------------------------------------------------------------------------- /lists/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /lists/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lists" 3 | version = "0.1.0" 4 | authors = ["ADesires"] 5 | -------------------------------------------------------------------------------- /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 = *mut Node; 9 | 10 | struct Node { 11 | elem: T, 12 | next: Link, 13 | } 14 | 15 | pub struct IntoIter(List); 16 | 17 | pub struct Iter<'a, T> { 18 | next: Option<&'a Node>, 19 | } 20 | 21 | pub struct IterMut<'a, T> { 22 | next: Option<&'a mut Node>, 23 | } 24 | 25 | impl List { 26 | pub fn new() -> Self { 27 | List { head: ptr::null_mut(), tail: ptr::null_mut() } 28 | } 29 | pub fn push(&mut self, elem: T) { 30 | unsafe { 31 | let new_tail = Box::into_raw(Box::new(Node { 32 | elem: elem, 33 | next: ptr::null_mut(), 34 | })); 35 | 36 | if !self.tail.is_null() { 37 | (*self.tail).next = new_tail; 38 | } else { 39 | self.head = new_tail; 40 | } 41 | 42 | self.tail = new_tail; 43 | } 44 | } 45 | pub fn pop(&mut self) -> Option { 46 | unsafe { 47 | if self.head.is_null() { 48 | None 49 | } else { 50 | let head = Box::from_raw(self.head); 51 | self.head = head.next; 52 | 53 | if self.head.is_null() { 54 | self.tail = ptr::null_mut(); 55 | } 56 | 57 | Some(head.elem) 58 | } 59 | } 60 | } 61 | 62 | pub fn peek(&self) -> Option<&T> { 63 | unsafe { 64 | self.head.as_ref().map(|node| &node.elem) 65 | } 66 | } 67 | 68 | pub fn peek_mut(&mut self) -> Option<&mut T> { 69 | unsafe { 70 | self.head.as_mut().map(|node| &mut node.elem) 71 | } 72 | } 73 | 74 | pub fn into_iter(self) -> IntoIter { 75 | IntoIter(self) 76 | } 77 | 78 | pub fn iter(&self) -> Iter<'_, T> { 79 | unsafe { 80 | Iter { next: self.head.as_ref() } 81 | } 82 | } 83 | 84 | pub fn iter_mut(&mut self) -> IterMut<'_, T> { 85 | unsafe { 86 | IterMut { next: self.head.as_mut() } 87 | } 88 | } 89 | } 90 | 91 | impl Drop for List { 92 | fn drop(&mut self) { 93 | while let Some(_) = self.pop() { } 94 | } 95 | } 96 | 97 | impl Iterator for IntoIter { 98 | type Item = T; 99 | fn next(&mut self) -> Option { 100 | self.0.pop() 101 | } 102 | } 103 | 104 | impl<'a, T> Iterator for Iter<'a, T> { 105 | type Item = &'a T; 106 | 107 | fn next(&mut self) -> Option { 108 | unsafe { 109 | self.next.map(|node| { 110 | self.next = node.next.as_ref(); 111 | &node.elem 112 | }) 113 | } 114 | } 115 | } 116 | 117 | impl<'a, T> Iterator for IterMut<'a, T> { 118 | type Item = &'a mut T; 119 | 120 | fn next(&mut self) -> Option { 121 | unsafe { 122 | self.next.take().map(|node| { 123 | self.next = node.next.as_mut(); 124 | &mut node.elem 125 | }) 126 | } 127 | } 128 | } 129 | 130 | #[cfg(test)] 131 | mod test { 132 | use super::List; 133 | #[test] 134 | fn basics() { 135 | let mut list = List::new(); 136 | 137 | // Check empty list behaves right 138 | assert_eq!(list.pop(), None); 139 | 140 | // Populate list 141 | list.push(1); 142 | list.push(2); 143 | list.push(3); 144 | 145 | // Check normal removal 146 | assert_eq!(list.pop(), Some(1)); 147 | assert_eq!(list.pop(), Some(2)); 148 | 149 | // Push some more just to make sure nothing's corrupted 150 | list.push(4); 151 | list.push(5); 152 | 153 | // Check normal removal 154 | assert_eq!(list.pop(), Some(3)); 155 | assert_eq!(list.pop(), Some(4)); 156 | 157 | // Check exhaustion 158 | assert_eq!(list.pop(), Some(5)); 159 | assert_eq!(list.pop(), None); 160 | 161 | // Check the exhaustion case fixed the pointer right 162 | list.push(6); 163 | list.push(7); 164 | 165 | // Check normal removal 166 | assert_eq!(list.pop(), Some(6)); 167 | assert_eq!(list.pop(), Some(7)); 168 | assert_eq!(list.pop(), None); 169 | } 170 | 171 | #[test] 172 | fn into_iter() { 173 | let mut list = List::new(); 174 | list.push(1); list.push(2); list.push(3); 175 | 176 | let mut iter = list.into_iter(); 177 | assert_eq!(iter.next(), Some(1)); 178 | assert_eq!(iter.next(), Some(2)); 179 | assert_eq!(iter.next(), Some(3)); 180 | assert_eq!(iter.next(), None); 181 | } 182 | 183 | #[test] 184 | fn iter() { 185 | let mut list = List::new(); 186 | list.push(1); list.push(2); list.push(3); 187 | 188 | let mut iter = list.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_mut() { 197 | let mut list = List::new(); 198 | list.push(1); list.push(2); list.push(3); 199 | 200 | let mut iter = list.iter_mut(); 201 | assert_eq!(iter.next(), Some(&mut 1)); 202 | assert_eq!(iter.next(), Some(&mut 2)); 203 | assert_eq!(iter.next(), Some(&mut 3)); 204 | assert_eq!(iter.next(), None); 205 | } 206 | 207 | #[test] 208 | fn miri_food() { 209 | let mut list = List::new(); 210 | 211 | list.push(1); 212 | list.push(2); 213 | list.push(3); 214 | 215 | assert!(list.pop() == Some(1)); 216 | list.push(4); 217 | assert!(list.pop() == Some(2)); 218 | list.push(5); 219 | 220 | assert!(list.peek() == Some(&3)); 221 | list.push(6); 222 | list.peek_mut().map(|x| *x *= 10); 223 | assert!(list.peek() == Some(&30)); 224 | assert!(list.pop() == Some(30)); 225 | 226 | for elem in list.iter_mut() { 227 | *elem *= 100; 228 | } 229 | 230 | let mut iter = list.iter(); 231 | assert_eq!(iter.next(), Some(&400)); 232 | assert_eq!(iter.next(), Some(&500)); 233 | assert_eq!(iter.next(), Some(&600)); 234 | assert_eq!(iter.next(), None); 235 | assert_eq!(iter.next(), None); 236 | 237 | assert!(list.pop() == Some(400)); 238 | list.peek_mut().map(|x| *x *= 10); 239 | assert!(list.peek() == Some(&5000)); 240 | list.push(7); 241 | 242 | // Drop it on the ground and let the dtor exercise itself 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /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 | self.head = node.next; 36 | Some(node.elem) 37 | } 38 | } 39 | } 40 | } 41 | 42 | impl Drop for List { 43 | fn drop(&mut self) { 44 | let mut cur_link = mem::replace(&mut self.head, Link::Empty); 45 | 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/fourth.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::cell::{Ref, RefMut, RefCell}; 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 | 18 | impl Node { 19 | fn new(elem: T) -> Rc> { 20 | Rc::new(RefCell::new(Node { 21 | elem: elem, 22 | prev: None, 23 | next: None, 24 | })) 25 | } 26 | } 27 | 28 | impl List { 29 | pub fn new() -> Self { 30 | List { head: None, tail: None } 31 | } 32 | 33 | pub fn push_front(&mut self, elem: T) { 34 | let new_head = Node::new(elem); 35 | match self.head.take() { 36 | Some(old_head) => { 37 | old_head.borrow_mut().prev = Some(new_head.clone()); 38 | new_head.borrow_mut().next = Some(old_head); 39 | self.head = Some(new_head); 40 | } 41 | None => { 42 | self.tail = Some(new_head.clone()); 43 | self.head = Some(new_head); 44 | } 45 | } 46 | } 47 | 48 | pub fn push_back(&mut self, elem: T) { 49 | let new_tail = Node::new(elem); 50 | match self.tail.take() { 51 | Some(old_tail) => { 52 | old_tail.borrow_mut().next = Some(new_tail.clone()); 53 | new_tail.borrow_mut().prev = Some(old_tail); 54 | self.tail = Some(new_tail); 55 | } 56 | None => { 57 | self.head = Some(new_tail.clone()); 58 | self.tail = Some(new_tail); 59 | } 60 | } 61 | } 62 | 63 | pub fn pop_back(&mut self) -> Option { 64 | self.tail.take().map(|old_tail| { 65 | match old_tail.borrow_mut().prev.take() { 66 | Some(new_tail) => { 67 | new_tail.borrow_mut().next.take(); 68 | self.tail = Some(new_tail); 69 | } 70 | None => { 71 | self.head.take(); 72 | } 73 | } 74 | Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem 75 | }) 76 | } 77 | 78 | pub fn pop_front(&mut self) -> Option { 79 | self.head.take().map(|old_head| { 80 | match old_head.borrow_mut().next.take() { 81 | Some(new_head) => { 82 | new_head.borrow_mut().prev.take(); 83 | self.head = Some(new_head); 84 | } 85 | None => { 86 | self.tail.take(); 87 | } 88 | } 89 | Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem 90 | }) 91 | } 92 | 93 | pub fn peek_front(&self) -> Option> { 94 | self.head.as_ref().map(|node| { 95 | Ref::map(node.borrow(), |node| &node.elem) 96 | }) 97 | } 98 | 99 | pub fn peek_back(&self) -> Option> { 100 | self.tail.as_ref().map(|node| { 101 | Ref::map(node.borrow(), |node| &node.elem) 102 | }) 103 | } 104 | 105 | pub fn peek_back_mut(&mut self) -> Option> { 106 | self.tail.as_ref().map(|node| { 107 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 108 | }) 109 | } 110 | 111 | pub fn peek_front_mut(&mut self) -> Option> { 112 | self.head.as_ref().map(|node| { 113 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 114 | }) 115 | } 116 | 117 | pub fn into_iter(self) -> IntoIter { 118 | IntoIter(self) 119 | } 120 | } 121 | 122 | impl Drop for List { 123 | fn drop(&mut self) { 124 | while self.pop_front().is_some() {} 125 | } 126 | } 127 | 128 | pub struct IntoIter(List); 129 | 130 | impl Iterator for IntoIter { 131 | type Item = T; 132 | 133 | fn next(&mut self) -> Option { 134 | self.0.pop_front() 135 | } 136 | } 137 | 138 | impl DoubleEndedIterator for IntoIter { 139 | fn next_back(&mut self) -> Option { 140 | self.0.pop_back() 141 | } 142 | } 143 | 144 | #[cfg(test)] 145 | mod test { 146 | use super::List; 147 | 148 | #[test] 149 | fn basics() { 150 | let mut list = List::new(); 151 | 152 | // Check empty list behaves right 153 | assert_eq!(list.pop_front(), None); 154 | 155 | // Populate list 156 | list.push_front(1); 157 | list.push_front(2); 158 | list.push_front(3); 159 | 160 | // Check normal removal 161 | assert_eq!(list.pop_front(), Some(3)); 162 | assert_eq!(list.pop_front(), Some(2)); 163 | 164 | // Push some more just to make sure nothing's corrupted 165 | list.push_front(4); 166 | list.push_front(5); 167 | 168 | // Check normal removal 169 | assert_eq!(list.pop_front(), Some(5)); 170 | assert_eq!(list.pop_front(), Some(4)); 171 | 172 | // Check exhaustion 173 | assert_eq!(list.pop_front(), Some(1)); 174 | assert_eq!(list.pop_front(), None); 175 | 176 | // ---- back ----- 177 | 178 | // Check empty list behaves right 179 | assert_eq!(list.pop_back(), None); 180 | 181 | // Populate list 182 | list.push_back(1); 183 | list.push_back(2); 184 | list.push_back(3); 185 | 186 | // Check normal removal 187 | assert_eq!(list.pop_back(), Some(3)); 188 | assert_eq!(list.pop_back(), Some(2)); 189 | 190 | // Push some more just to make sure nothing's corrupted 191 | list.push_back(4); 192 | list.push_back(5); 193 | 194 | // Check normal removal 195 | assert_eq!(list.pop_back(), Some(5)); 196 | assert_eq!(list.pop_back(), Some(4)); 197 | 198 | // Check exhaustion 199 | assert_eq!(list.pop_back(), Some(1)); 200 | assert_eq!(list.pop_back(), None); 201 | } 202 | 203 | #[test] 204 | fn peek() { 205 | let mut list = List::new(); 206 | assert!(list.peek_front().is_none()); 207 | assert!(list.peek_back().is_none()); 208 | assert!(list.peek_front_mut().is_none()); 209 | assert!(list.peek_back_mut().is_none()); 210 | 211 | list.push_front(1); list.push_front(2); list.push_front(3); 212 | 213 | assert_eq!(&*list.peek_front().unwrap(), &3); 214 | assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); 215 | assert_eq!(&*list.peek_back().unwrap(), &1); 216 | assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); 217 | } 218 | 219 | #[test] 220 | fn into_iter() { 221 | let mut list = List::new(); 222 | list.push_front(1); list.push_front(2); list.push_front(3); 223 | 224 | let mut iter = list.into_iter(); 225 | assert_eq!(iter.next(), Some(3)); 226 | assert_eq!(iter.next_back(), Some(1)); 227 | assert_eq!(iter.next(), Some(2)); 228 | assert_eq!(iter.next_back(), None); 229 | assert_eq!(iter.next(), None); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /lists/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod first; 2 | pub mod second; 3 | pub mod third; 4 | pub mod fourth; 5 | pub mod fifth; 6 | 7 | pub mod silly1; 8 | pub mod silly2; -------------------------------------------------------------------------------- /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 | impl List { 13 | pub fn new() -> Self { 14 | List { head: None } 15 | } 16 | 17 | pub fn push(&mut self, elem: T) { 18 | let new_node = Box::new(Node { 19 | elem: elem, 20 | next: self.head.take(), 21 | }); 22 | 23 | self.head = Some(new_node); 24 | } 25 | 26 | pub fn pop(&mut self) -> Option { 27 | self.head.take().map(|node| { 28 | self.head = node.next; 29 | node.elem 30 | }) 31 | } 32 | 33 | pub fn peek(&self) -> Option<&T> { 34 | self.head.as_ref().map(|node| { 35 | &node.elem 36 | }) 37 | } 38 | 39 | pub fn peek_mut(&mut self) -> Option<&mut T> { 40 | self.head.as_mut().map(|node| { 41 | &mut node.elem 42 | }) 43 | } 44 | 45 | pub fn into_iter(self) -> IntoIter { 46 | IntoIter(self) 47 | } 48 | 49 | pub fn iter(&self) -> Iter<'_, T> { 50 | Iter { next: self.head.as_deref() } 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 | pub struct IntoIter(List); 64 | 65 | impl Iterator for IntoIter { 66 | type Item = T; 67 | fn next(&mut self) -> Option { 68 | self.0.pop() 69 | } 70 | } 71 | 72 | pub struct Iter<'a, T> { 73 | next: Option<&'a Node>, 74 | } 75 | 76 | impl<'a, T> Iterator for Iter<'a, T> { 77 | type Item = &'a T; 78 | fn next(&mut self) -> Option { 79 | self.next.map(|node| { 80 | self.next = node.next.as_deref(); 81 | &node.elem 82 | }) 83 | } 84 | } 85 | 86 | pub struct IterMut<'a, T: 'a> { 87 | next: Option<&'a mut Node>, 88 | } 89 | 90 | impl List { 91 | pub fn iter_mut(&mut self) -> IterMut { 92 | IterMut { next: self.head.as_deref_mut() } 93 | } 94 | } 95 | 96 | impl<'a, T> Iterator for IterMut<'a, T> { 97 | type Item = &'a mut T; 98 | 99 | fn next(&mut self) -> Option { 100 | self.next.take().map(|node| { 101 | self.next = node.next.as_deref_mut(); 102 | &mut node.elem 103 | }) 104 | } 105 | } 106 | 107 | #[cfg(test)] 108 | mod test { 109 | use super::List; 110 | 111 | #[test] 112 | fn basics() { 113 | let mut list = List::new(); 114 | 115 | // Check empty list behaves right 116 | assert_eq!(list.pop(), None); 117 | 118 | // Populate list 119 | list.push(1); 120 | list.push(2); 121 | list.push(3); 122 | 123 | // Check normal removal 124 | assert_eq!(list.pop(), Some(3)); 125 | assert_eq!(list.pop(), Some(2)); 126 | 127 | // Push some more just to make sure nothing's corrupted 128 | list.push(4); 129 | list.push(5); 130 | 131 | // Check normal removal 132 | assert_eq!(list.pop(), Some(5)); 133 | assert_eq!(list.pop(), Some(4)); 134 | 135 | // Check exhaustion 136 | assert_eq!(list.pop(), Some(1)); 137 | assert_eq!(list.pop(), None); 138 | } 139 | 140 | #[test] 141 | fn peek() { 142 | let mut list = List::new(); 143 | assert_eq!(list.peek(), None); 144 | assert_eq!(list.peek_mut(), None); 145 | list.push(1); list.push(2); list.push(3); 146 | 147 | assert_eq!(list.peek(), Some(&3)); 148 | assert_eq!(list.peek_mut(), Some(&mut 3)); 149 | 150 | list.peek_mut().map(|value| { 151 | *value = 42 152 | }); 153 | 154 | assert_eq!(list.peek(), Some(&42)); 155 | assert_eq!(list.pop(), Some(42)); 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 | assert_eq!(iter.next(), None); 168 | } 169 | 170 | #[test] 171 | fn iter() { 172 | let mut list = List::new(); 173 | list.push(1); list.push(2); list.push(3); 174 | 175 | let mut iter = list.iter(); 176 | assert_eq!(iter.next(), Some(&3)); 177 | assert_eq!(iter.next(), Some(&2)); 178 | assert_eq!(iter.next(), Some(&1)); 179 | } 180 | 181 | #[test] 182 | fn iter_mut() { 183 | let mut list = List::new(); 184 | list.push(1); list.push(2); list.push(3); 185 | 186 | let mut iter = list.iter_mut(); 187 | assert_eq!(iter.next(), Some(&mut 3)); 188 | assert_eq!(iter.next(), Some(&mut 2)); 189 | assert_eq!(iter.next(), Some(&mut 1)); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lists/src/silly2.rs: -------------------------------------------------------------------------------- 1 | pub struct List<'a, T> { 2 | pub data: T, 3 | pub prev: Option<&'a List<'a, T>>, 4 | } 5 | 6 | pub struct Iter<'a, T> { 7 | next: Option<&'a List<'a, T>>, 8 | } 9 | 10 | impl<'a, T> List<'a, T> { 11 | pub fn push( 12 | prev: Option<&'a List<'a, T>>, 13 | data: T, 14 | callback: impl FnOnce(&List<'a, T>) -> U, 15 | ) -> U { 16 | let list = List { data, prev }; 17 | callback(&list) 18 | } 19 | 20 | pub fn iter(&'a self) -> Iter<'a, T> { 21 | Iter { next: Some(self) } 22 | } 23 | } 24 | 25 | impl<'a, T> Iterator for Iter<'a, T> { 26 | type Item = &'a T; 27 | 28 | fn next(&mut self) -> Option { 29 | self.next.map(|node| { 30 | self.next = node.prev; 31 | &node.data 32 | }) 33 | } 34 | } 35 | 36 | #[cfg(test)] 37 | mod test { 38 | use super::List; 39 | 40 | #[test] 41 | fn elegance() { 42 | List::push(None, 3, |list| { 43 | assert_eq!(list.iter().copied().sum::(), 3); 44 | List::push(Some(list), 5, |list| { 45 | assert_eq!(list.iter().copied().sum::(), 5 + 3); 46 | List::push(Some(list), 13, |list| { 47 | assert_eq!(list.iter().copied().sum::(), 13 + 5 + 3); 48 | }) 49 | }) 50 | }) 51 | } 52 | 53 | #[test] 54 | fn cell() { 55 | use std::cell::Cell; 56 | 57 | List::push(None, Cell::new(3), |list| { 58 | List::push(Some(list), Cell::new(5), |list| { 59 | List::push(Some(list), Cell::new(13), |list| { 60 | // Multiply every value in the list by 10 61 | for val in list.iter() { 62 | val.set(val.get() * 10) 63 | } 64 | 65 | let mut vals = list.iter(); 66 | assert_eq!(vals.next().unwrap().get(), 130); 67 | assert_eq!(vals.next().unwrap().get(), 50); 68 | assert_eq!(vals.next().unwrap().get(), 30); 69 | assert_eq!(vals.next(), None); 70 | assert_eq!(vals.next(), None); 71 | }) 72 | }) 73 | }) 74 | } 75 | } -------------------------------------------------------------------------------- /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 | impl List { 15 | pub fn new() -> Self { 16 | List { head: None } 17 | } 18 | 19 | pub fn prepend(&self, elem: T) -> List { 20 | List { head: Some(Rc::new(Node { 21 | elem: elem, 22 | next: self.head.clone(), 23 | }))} 24 | } 25 | 26 | pub fn tail(&self) -> List { 27 | List { head: self.head.as_ref().and_then(|node| node.next.clone()) } 28 | } 29 | 30 | pub fn head(&self) -> Option<&T> { 31 | self.head.as_ref().map(|node| &node.elem) 32 | } 33 | 34 | pub fn iter(&self) -> Iter<'_, T> { 35 | Iter { next: self.head.as_deref() } 36 | } 37 | } 38 | 39 | impl Drop for List { 40 | fn drop(&mut self) { 41 | let mut head = self.head.take(); 42 | while let Some(node) = head { 43 | if let Ok(mut node) = Rc::try_unwrap(node) { 44 | head = node.next.take(); 45 | } else { 46 | break; 47 | } 48 | } 49 | } 50 | } 51 | 52 | pub struct Iter<'a, T> { 53 | next: Option<&'a Node>, 54 | } 55 | 56 | impl<'a, T> Iterator for Iter<'a, T> { 57 | type Item = &'a T; 58 | 59 | fn next(&mut self) -> Option { 60 | self.next.map(|node| { 61 | self.next = node.next.as_deref(); 62 | &node.elem 63 | }) 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod test { 69 | use super::List; 70 | 71 | #[test] 72 | fn basics() { 73 | let list = List::new(); 74 | assert_eq!(list.head(), None); 75 | 76 | let list = list.prepend(1).prepend(2).prepend(3); 77 | assert_eq!(list.head(), Some(&3)); 78 | 79 | let list = list.tail(); 80 | assert_eq!(list.head(), Some(&2)); 81 | 82 | let list = list.tail(); 83 | assert_eq!(list.head(), Some(&1)); 84 | 85 | let list = list.tail(); 86 | assert_eq!(list.head(), None); 87 | 88 | // Make sure empty tail works 89 | let list = list.tail(); 90 | assert_eq!(list.head(), None); 91 | } 92 | 93 | #[test] 94 | fn iter() { 95 | let list = List::new().prepend(1).prepend(2).prepend(3); 96 | 97 | let mut iter = list.iter(); 98 | assert_eq!(iter.next(), Some(&3)); 99 | assert_eq!(iter.next(), Some(&2)); 100 | assert_eq!(iter.next(), Some(&1)); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](README.md) 4 | * [A Bad Stack](first.md) 5 | * [Layout](first-layout.md) 6 | * [New](first-new.md) 7 | * [Ownership 101](first-ownership.md) 8 | * [Push](first-push.md) 9 | * [Pop](first-pop.md) 10 | * [Testing](first-test.md) 11 | * [Drop](first-drop.md) 12 | * [Final Code](first-final.md) 13 | * [An Ok Stack](second.md) 14 | * [Option](second-option.md) 15 | * [Generic](second-generic.md) 16 | * [Peek](second-peek.md) 17 | * [IntoIter](second-into-iter.md) 18 | * [Iter](second-iter.md) 19 | * [IterMut](second-iter-mut.md) 20 | * [Final Code](second-final.md) 21 | * [A Persistent Stack](third.md) 22 | * [Layout](third-layout.md) 23 | * [Basics](third-basics.md) 24 | * [Drop](third-drop.md) 25 | * [Arc](third-arc.md) 26 | * [Final Code](third-final.md) 27 | * [A Bad Safe Deque](fourth.md) 28 | * [Layout](fourth-layout.md) 29 | * [Building](fourth-building.md) 30 | * [Breaking](fourth-breaking.md) 31 | * [Peek](fourth-peek.md) 32 | * [Symmetric Cases](fourth-symmetry.md) 33 | * [Iteration](fourth-iteration.md) 34 | * [Final Code](fourth-final.md) 35 | * [An Ok Unsafe Queue](fifth.md) 36 | * [Layout](fifth-layout.md) 37 | * [Unsafe](fifth-unsafe.md) 38 | * [Basics](fifth-basics.md) 39 | * [Miri](fifth-miri.md) 40 | * [Stacked Borrows](fifth-stacked-borrows.md) 41 | * [Testing Stacked Borrows](fifth-testing-stacked-borrows.md) 42 | * [Layout + Basics Redux](fifth-layout-basics-redux.md) 43 | * [Extras](fifth-extras.md) 44 | * [Final Code](fifth-final.md) 45 | * [A Production Unsafe Deque](sixth.md) 46 | * [Layout](sixth-layout.md) 47 | * [Variance and Subtyping](sixth-variance.md) 48 | * [Basics](sixth-basics.md) 49 | * [Panic Safety](sixth-panics.md) 50 | * [Boring Combinatorics](sixth-combinatorics.md) 51 | * [Filling In Random Bits](sixth-random-bits.md) 52 | * [Testing](sixth-testing.md) 53 | * [Send, Sync, and Compile Tests](sixth-send-sync.md) 54 | * [An Introduction To Cursors](sixth-cursors-intro.md) 55 | * [Implementing Cursors](sixth-cursors-impl.md) 56 | * [Testing Cursors](sixth-cursors-testing.md) 57 | * [Final Code](sixth-final.md) 58 | * [A Bunch of Silly Lists](infinity.md) 59 | * [The Double Single](infinity-double-single.md) 60 | * [The Stack-Allocated Linked List](infinity-stack-allocated.md) 61 | -------------------------------------------------------------------------------- /src/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 | You know, except for that part where miri completely dunked on us and we had to write a short master's thesis on rust's memory model. You know, as you do. 8 | 9 | But on the bright side we *didn't* have to write any jank Rc or RefCell stuff. 10 | 11 | ```rust 12 | use std::ptr; 13 | 14 | pub struct List { 15 | head: Link, 16 | tail: *mut Node, 17 | } 18 | 19 | type Link = *mut Node; 20 | 21 | struct Node { 22 | elem: T, 23 | next: Link, 24 | } 25 | 26 | pub struct IntoIter(List); 27 | 28 | pub struct Iter<'a, T> { 29 | next: Option<&'a Node>, 30 | } 31 | 32 | pub struct IterMut<'a, T> { 33 | next: Option<&'a mut Node>, 34 | } 35 | 36 | impl List { 37 | pub fn new() -> Self { 38 | List { head: ptr::null_mut(), tail: ptr::null_mut() } 39 | } 40 | pub fn push(&mut self, elem: T) { 41 | unsafe { 42 | let new_tail = Box::into_raw(Box::new(Node { 43 | elem: elem, 44 | next: ptr::null_mut(), 45 | })); 46 | 47 | if !self.tail.is_null() { 48 | (*self.tail).next = new_tail; 49 | } else { 50 | self.head = new_tail; 51 | } 52 | 53 | self.tail = new_tail; 54 | } 55 | } 56 | pub fn pop(&mut self) -> Option { 57 | unsafe { 58 | if self.head.is_null() { 59 | None 60 | } else { 61 | let head = Box::from_raw(self.head); 62 | self.head = head.next; 63 | 64 | if self.head.is_null() { 65 | self.tail = ptr::null_mut(); 66 | } 67 | 68 | Some(head.elem) 69 | } 70 | } 71 | } 72 | 73 | pub fn peek(&self) -> Option<&T> { 74 | unsafe { 75 | self.head.as_ref().map(|node| &node.elem) 76 | } 77 | } 78 | 79 | pub fn peek_mut(&mut self) -> Option<&mut T> { 80 | unsafe { 81 | self.head.as_mut().map(|node| &mut node.elem) 82 | } 83 | } 84 | 85 | pub fn into_iter(self) -> IntoIter { 86 | IntoIter(self) 87 | } 88 | 89 | pub fn iter(&self) -> Iter<'_, T> { 90 | unsafe { 91 | Iter { next: self.head.as_ref() } 92 | } 93 | } 94 | 95 | pub fn iter_mut(&mut self) -> IterMut<'_, T> { 96 | unsafe { 97 | IterMut { next: self.head.as_mut() } 98 | } 99 | } 100 | } 101 | 102 | impl Drop for List { 103 | fn drop(&mut self) { 104 | while let Some(_) = self.pop() { } 105 | } 106 | } 107 | 108 | impl Iterator for IntoIter { 109 | type Item = T; 110 | fn next(&mut self) -> Option { 111 | self.0.pop() 112 | } 113 | } 114 | 115 | impl<'a, T> Iterator for Iter<'a, T> { 116 | type Item = &'a T; 117 | 118 | fn next(&mut self) -> Option { 119 | unsafe { 120 | self.next.map(|node| { 121 | self.next = node.next.as_ref(); 122 | &node.elem 123 | }) 124 | } 125 | } 126 | } 127 | 128 | impl<'a, T> Iterator for IterMut<'a, T> { 129 | type Item = &'a mut T; 130 | 131 | fn next(&mut self) -> Option { 132 | unsafe { 133 | self.next.take().map(|node| { 134 | self.next = node.next.as_mut(); 135 | &mut node.elem 136 | }) 137 | } 138 | } 139 | } 140 | 141 | #[cfg(test)] 142 | mod test { 143 | use super::List; 144 | #[test] 145 | fn basics() { 146 | let mut list = List::new(); 147 | 148 | // Check empty list behaves right 149 | assert_eq!(list.pop(), None); 150 | 151 | // Populate list 152 | list.push(1); 153 | list.push(2); 154 | list.push(3); 155 | 156 | // Check normal removal 157 | assert_eq!(list.pop(), Some(1)); 158 | assert_eq!(list.pop(), Some(2)); 159 | 160 | // Push some more just to make sure nothing's corrupted 161 | list.push(4); 162 | list.push(5); 163 | 164 | // Check normal removal 165 | assert_eq!(list.pop(), Some(3)); 166 | assert_eq!(list.pop(), Some(4)); 167 | 168 | // Check exhaustion 169 | assert_eq!(list.pop(), Some(5)); 170 | assert_eq!(list.pop(), None); 171 | 172 | // Check the exhaustion case fixed the pointer right 173 | list.push(6); 174 | list.push(7); 175 | 176 | // Check normal removal 177 | assert_eq!(list.pop(), Some(6)); 178 | assert_eq!(list.pop(), Some(7)); 179 | assert_eq!(list.pop(), None); 180 | } 181 | 182 | #[test] 183 | fn into_iter() { 184 | let mut list = List::new(); 185 | list.push(1); list.push(2); list.push(3); 186 | 187 | let mut iter = list.into_iter(); 188 | assert_eq!(iter.next(), Some(1)); 189 | assert_eq!(iter.next(), Some(2)); 190 | assert_eq!(iter.next(), Some(3)); 191 | assert_eq!(iter.next(), None); 192 | } 193 | 194 | #[test] 195 | fn iter() { 196 | let mut list = List::new(); 197 | list.push(1); list.push(2); list.push(3); 198 | 199 | let mut iter = list.iter(); 200 | assert_eq!(iter.next(), Some(&1)); 201 | assert_eq!(iter.next(), Some(&2)); 202 | assert_eq!(iter.next(), Some(&3)); 203 | assert_eq!(iter.next(), None); 204 | } 205 | 206 | #[test] 207 | fn iter_mut() { 208 | let mut list = List::new(); 209 | list.push(1); list.push(2); list.push(3); 210 | 211 | let mut iter = list.iter_mut(); 212 | assert_eq!(iter.next(), Some(&mut 1)); 213 | assert_eq!(iter.next(), Some(&mut 2)); 214 | assert_eq!(iter.next(), Some(&mut 3)); 215 | assert_eq!(iter.next(), None); 216 | } 217 | 218 | #[test] 219 | fn miri_food() { 220 | let mut list = List::new(); 221 | 222 | list.push(1); 223 | list.push(2); 224 | list.push(3); 225 | 226 | assert!(list.pop() == Some(1)); 227 | list.push(4); 228 | assert!(list.pop() == Some(2)); 229 | list.push(5); 230 | 231 | assert!(list.peek() == Some(&3)); 232 | list.push(6); 233 | list.peek_mut().map(|x| *x *= 10); 234 | assert!(list.peek() == Some(&30)); 235 | assert!(list.pop() == Some(30)); 236 | 237 | for elem in list.iter_mut() { 238 | *elem *= 100; 239 | } 240 | 241 | let mut iter = list.iter(); 242 | assert_eq!(iter.next(), Some(&400)); 243 | assert_eq!(iter.next(), Some(&500)); 244 | assert_eq!(iter.next(), Some(&600)); 245 | assert_eq!(iter.next(), None); 246 | assert_eq!(iter.next(), None); 247 | 248 | assert!(list.pop() == Some(400)); 249 | list.peek_mut().map(|x| *x *= 10); 250 | assert!(list.peek() == Some(&5000)); 251 | list.push(7); 252 | 253 | // Drop it on the ground and let the dtor exercise itself 254 | } 255 | } 256 | ``` 257 | -------------------------------------------------------------------------------- /src/fifth-layout-basics-redux.md: -------------------------------------------------------------------------------- 1 | # Layout and Basics 2: Getting Raw 2 | 3 | > TL;DR on the previous three sections: randomly mixing safe pointers like `&`, `&mut`, and `Box` with unsafe pointers like `*mut` and `*const` is a recipe for Undefined Behaviour because the safe pointers introduce extra constraints that we aren't obeying with the raw pointers. 4 | 5 | Oh god I need to write linked lists again. Fine. FINE. It's Fine. We're fine. 6 | 7 | We're gonna knock a lot of this section out real quick since we already discussed the design in the first try around, and everything we did *was* basically correct except for how we mixed together safe and unsafe pointers. 8 | 9 | 10 | # Layout 11 | 12 | So in the new layout we're only going to only use raw pointers and everything will be perfect and we'll never make mistakes again. 13 | 14 | Here's our old broken layout: 15 | 16 | ```rust 17 | pub struct List { 18 | head: Link, 19 | tail: *mut Node, // INNOCENT AND KIND 20 | } 21 | 22 | type Link = Option>>; // THE REAL EVIL 23 | 24 | struct Node { 25 | elem: T, 26 | next: Link, 27 | } 28 | ``` 29 | 30 | And here's our new layout: 31 | 32 | ```rust 33 | pub struct List { 34 | head: Link, 35 | tail: *mut Node, 36 | } 37 | 38 | type Link = *mut Node; // MUCH BETTER 39 | 40 | struct Node { 41 | elem: T, 42 | next: Link, 43 | } 44 | ``` 45 | 46 | Remember: Option isn't as nice or useful when we're using raw pointers, so we're not using that anymore. In later sections we'll look at the `NonNull` type, but don't worry about that for now. 47 | 48 | 49 | 50 | # Basics 51 | 52 | List::new is basically the same. 53 | 54 | ```rust ,ignore 55 | use ptr; 56 | 57 | impl List { 58 | pub fn new() -> Self { 59 | List { head: ptr::null_mut(), tail: ptr::null_mut() } 60 | } 61 | } 62 | ``` 63 | 64 | Push is basically the s- 65 | 66 | 67 | ```rust ,ignore 68 | pub fn push(&mut self, elem: T) { 69 | let mut new_tail = Box::new( 70 | ``` 71 | 72 | Wait we're not using Box anymore. How do we allocate memory without Box? 73 | 74 | Well, we *could* with `std::alloc::alloc`, but that's like bringing a katana into the kitchen. It'll get the job done but it's kinda overkill and unwieldy. 75 | 76 | We want to *have* boxes, but, *not*. One completely wild but *maybe* viable option would be to do something like this: 77 | 78 | ``` 79 | struct Node { 80 | elem: T, 81 | real_next: Option>>, 82 | next: *mut Node, 83 | } 84 | ``` 85 | 86 | With the idea that we create the Boxes and store them in our node, but then we take a raw pointer into them and only use that raw pointer until we're done with the Node and want to destroy it. Then we can `take` the Box out of `real_next` and drop it. I *think* that would conform to our very simplified stacked borrows model? 87 | 88 | If you wanna try to make that, have "fun", but that just looks awful right? This isn't the chapter on Rc and RefCell, we're not gonna play this *game* anymore. We're gonna just make simple and clean stuff. 89 | 90 | So instead we're going to use the very nice [Box::into_raw][] function: 91 | 92 | > ```rust ,ignore 93 | > pub fn into_raw(b: Box) -> *mut T 94 | > ``` 95 | > 96 | > Consumes the Box, returning a wrapped raw pointer. 97 | > 98 | > The pointer will be properly aligned and non-null. 99 | > 100 | >After calling this function, the caller is responsible for the memory previously managed by the Box. In particular, the caller should properly destroy T and release the memory, taking into account the memory layout used by Box. The easiest way to do this is to convert the raw pointer back into a Box with the `Box::from_raw` function, allowing the Box destructor to perform the cleanup. 101 | > 102 | > Note: this is an associated function, which means that you have to call it as `Box::into_raw(b)` instead of `b.into_raw()`. This is so that there is no conflict with a method on the inner type. 103 | > 104 | > **Examples** 105 | > 106 | > Converting the raw pointer back into a Box with Box::from_raw for automatic cleanup: 107 | > 108 | > ``` 109 | > let x = Box::new(String::from("Hello")); 110 | > let ptr = Box::into_raw(x); 111 | > let x = unsafe { Box::from_raw(ptr) }; 112 | > ``` 113 | 114 | Nice, that looks *literally* designed for our use case. It also matches the rules we're trying to follow: start with safe stuff, turn into into raw pointers, and then only convert back to safe stuff at the end (when we want to Drop it). 115 | 116 | This is basically exactly like doing the weird `real_next` thing but without having to faff around storing the Box when it's the exact same pointer as the raw pointer anyway. 117 | 118 | Also now that we're just using raw pointers everywhere, let's not worry about keeping those `unsafe` blocks narrow: it's all unsafe now. (It always was, but it's nice to lie to yourself sometimes.) 119 | 120 | 121 | ```rust ,ignore 122 | pub fn push(&mut self, elem: T) { 123 | unsafe { 124 | // Immediately convert the Box into a raw pointer 125 | let new_tail = Box::into_raw(Box::new(Node { 126 | elem: elem, 127 | next: ptr::null_mut(), 128 | })); 129 | 130 | if !self.tail.is_null() { 131 | (*self.tail).next = new_tail; 132 | } else { 133 | self.head = new_tail; 134 | } 135 | 136 | self.tail = new_tail; 137 | } 138 | } 139 | ``` 140 | 141 | 142 | Hey that code's actually looking a lot cleaner now that we're sticking to raw pointers! 143 | 144 | On to pop, which is also pretty similar to how we left it, although we've got to remember to use `Box::from_raw` to clean up the allocation: 145 | 146 | ```rust ,ignore 147 | pub fn pop(&mut self) -> Option { 148 | unsafe { 149 | if self.head.is_null() { 150 | None 151 | } else { 152 | // RISE FROM THE GRAVE 153 | let head = Box::from_raw(self.head); 154 | self.head = head.next; 155 | 156 | if self.head.is_null() { 157 | self.tail = ptr::null_mut(); 158 | } 159 | 160 | Some(head.elem) 161 | } 162 | } 163 | } 164 | ``` 165 | 166 | Our nice little `take`s and `map`s are dead, gotta just check and set `null` manually now. 167 | 168 | And while we're here, let's slap in the destructor. This time we'll implement it as just repeatedly popping, because it's cute and simple: 169 | 170 | ```rust ,ignore 171 | impl Drop for List { 172 | fn drop(&mut self) { 173 | while let Some(_) = self.pop() { } 174 | } 175 | } 176 | ``` 177 | 178 | 179 | Now, for the moment of truth: 180 | 181 | ```rust ,ignore 182 | #[cfg(test)] 183 | mod test { 184 | use super::List; 185 | #[test] 186 | fn basics() { 187 | let mut list = List::new(); 188 | 189 | // Check empty list behaves right 190 | assert_eq!(list.pop(), None); 191 | 192 | // Populate list 193 | list.push(1); 194 | list.push(2); 195 | list.push(3); 196 | 197 | // Check normal removal 198 | assert_eq!(list.pop(), Some(1)); 199 | assert_eq!(list.pop(), Some(2)); 200 | 201 | // Push some more just to make sure nothing's corrupted 202 | list.push(4); 203 | list.push(5); 204 | 205 | // Check normal removal 206 | assert_eq!(list.pop(), Some(3)); 207 | assert_eq!(list.pop(), Some(4)); 208 | 209 | // Check exhaustion 210 | assert_eq!(list.pop(), Some(5)); 211 | assert_eq!(list.pop(), None); 212 | 213 | // Check the exhaustion case fixed the pointer right 214 | list.push(6); 215 | list.push(7); 216 | 217 | // Check normal removal 218 | assert_eq!(list.pop(), Some(6)); 219 | assert_eq!(list.pop(), Some(7)); 220 | assert_eq!(list.pop(), None); 221 | } 222 | } 223 | ``` 224 | 225 | ```text 226 | cargo test 227 | 228 | running 12 tests 229 | test fifth::test::basics ... ok 230 | test first::test::basics ... ok 231 | test fourth::test::basics ... ok 232 | test fourth::test::peek ... ok 233 | test second::test::basics ... ok 234 | test fourth::test::into_iter ... ok 235 | test second::test::into_iter ... ok 236 | test second::test::iter ... ok 237 | test second::test::iter_mut ... ok 238 | test second::test::peek ... ok 239 | test third::test::basics ... ok 240 | test third::test::iter ... ok 241 | 242 | test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured 243 | ``` 244 | 245 | Good, but does miri agree? 246 | 247 | ```text 248 | MIRIFLAGS="-Zmiri-tag-raw-pointers" cargo +nightly-2022-01-21 miri test 249 | 250 | running 12 tests 251 | test fifth::test::basics ... ok 252 | test first::test::basics ... ok 253 | test fourth::test::basics ... ok 254 | test fourth::test::peek ... ok 255 | test second::test::basics ... ok 256 | test fourth::test::into_iter ... ok 257 | test second::test::into_iter ... ok 258 | test second::test::iter ... ok 259 | test second::test::iter_mut ... ok 260 | test second::test::peek ... ok 261 | test third::test::basics ... ok 262 | test third::test::iter ... ok 263 | 264 | test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured 265 | ``` 266 | 267 | EYYYY!!!!! 268 | 269 | IT FRIGGIN WORKED! 270 | 271 | PROBABLY! 272 | 273 | FAILING TO FIND UNDEFINED BEHAVIOUR IS NOT A PROOF THAT IT ISN'T THERE WAITING TO CAUSE PROBLEMS BUT THERE IS A LIMIT TO HOW RIGOROUS I AM WILLING TO BE FOR A JOKE BOOK ABOUT LINKED LISTS SO WE'RE GONNA CALL THIS A 100% MACHINE VERIFIED PROOF AND ANYONE WHO SAYS OTHERWISE CAN SUCK MY COQ! 274 | 275 | ∴ QED □ 276 | 277 | 278 | [Box::into_raw]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.into_raw 279 | -------------------------------------------------------------------------------- /src/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 | > **NARRATOR:** This was a lie, but it did seem true in 2015. 25 | 26 | The main Unsafe tool we'll be using are *raw pointers*. Raw pointers are 27 | basically C's pointers. They have no inherent aliasing rules. They have no 28 | lifetimes. They can be null. They can be misaligned. They can be dangling. They can point to 29 | uninitialized memory. They can be cast to and from integers. They can be cast 30 | to point to a different type. Mutability? Cast it. Pretty much everything goes, 31 | and that means pretty much anything can go wrong. 32 | 33 | > **NARRATOR:** no inherent aliasing rules, eh? Ah, the innocence of youth. 34 | 35 | This is some bad stuff and honestly you'll live a happier life never having 36 | to touch these. Unfortunately, we want to write linked lists, and linked lists 37 | are awful. That means we're going to have to use unsafe pointers. 38 | 39 | There are two kinds of raw pointer: `*const T` and `*mut T`. These are meant to 40 | be `const T*` and `T*` from C, but we really don't care about what C thinks they 41 | mean that much. You can only dereference a `*const T` to an `&T`, but much like 42 | the mutability of a variable, this is just a lint against incorrect usage. At 43 | most it just means you have to cast the `*const` to a `*mut` first. Although if 44 | you don't actually have permission to mutate the referent of the pointer, 45 | you're gonna have a bad time. 46 | 47 | Anyway, we'll get a better feel for this as we write some code. For now, 48 | `*mut T == &unchecked mut T`! 49 | 50 | [nom]: https://doc.rust-lang.org/nightly/nomicon/ 51 | -------------------------------------------------------------------------------- /src/fifth.md: -------------------------------------------------------------------------------- 1 | # An Ok 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 | > **NARRATOR:** And I will point out the mistakes. 14 | 15 | And we won't make *any* mistakes. 16 | 17 | Let's add a new file called `fifth.rs`: 18 | 19 | ```rust ,ignore 20 | // in lib.rs 21 | 22 | pub mod first; 23 | pub mod second; 24 | pub mod third; 25 | pub mod fourth; 26 | pub mod fifth; 27 | ``` 28 | 29 | Our code is largely going to be derived from second.rs, since a queue is 30 | mostly an augmentation of a stack in the world of linked lists. Still, we're 31 | going to go from scratch because there's some fundamental issues we want to 32 | address with layout and what-not. 33 | -------------------------------------------------------------------------------- /src/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 | ```rust ,ignore 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 ,ignore 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 ,ignore 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 | 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 | ``` 111 | 112 | Great! 113 | 114 | ---------------------- 115 | 116 | ![Bonus](img/profbee.gif) 117 | 118 | ## Bonus Section For Premature Optimization! 119 | 120 | Our implementation of drop is actually *very* similar to 121 | `while let Some(_) = self.pop() { }`, which is certainly simpler. How is 122 | it different, and what performance issues could result from it once we start 123 | generalizing our list to store things other than integers? 124 | 125 |
126 | Click to expand for answer 127 | 128 | Pop returns `Option`, while our implementation only manipulates Links (`Box`). So our implementation only moves around pointers to nodes, while the pop-based one will move around the values we stored in nodes. This could be very expensive if we generalize our list and someone uses it to store instances of VeryBigThingWithADropImpl (VBTWADI). Box is able to run the drop implementation of its contents in-place, so it doesn't suffer from this issue. Since VBTWADI is *exactly* the kind of thing that actually makes using a linked-list desirable over an array, behaving poorly on this case would be a bit of a disappointment. 129 | 130 | If you wish to have the best of both implementations, you could add a new method, 131 | `fn pop_node(&mut self) -> Link`, from-which `pop` and `drop` can both be cleanly derived. 132 | 133 |
134 | -------------------------------------------------------------------------------- /src/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 | self.head = node.next; 41 | Some(node.elem) 42 | } 43 | } 44 | } 45 | } 46 | 47 | impl Drop for List { 48 | fn drop(&mut self) { 49 | let mut cur_link = mem::replace(&mut self.head, Link::Empty); 50 | 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 | -------------------------------------------------------------------------------- /src/first-new.md: -------------------------------------------------------------------------------- 1 | # New 2 | 3 | To associate actual code with a type, we use `impl` blocks: 4 | 5 | ```rust ,ignore 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 ,ignore 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 ,ignore 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 the 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 | -------------------------------------------------------------------------------- /src/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 are 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 you leave it in a valid state when you're 31 | done (it would be rude to the owner otherwise!). 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 | -------------------------------------------------------------------------------- /src/first-pop.md: -------------------------------------------------------------------------------- 1 | # Pop 2 | 3 | Like `push`, `pop` wants to mutate the list. Unlike `push`, we actually 4 | want to return something. But `pop` also has to deal with a tricky corner 5 | case: what if the list is empty? To represent this case, we use the trusty 6 | `Option` type: 7 | 8 | ```rust ,ignore 9 | pub fn pop(&mut self) -> Option { 10 | // TODO 11 | } 12 | ``` 13 | 14 | `Option` is an enum that represents a value that may exist. It can either be 15 | `Some(T)` or `None`. We could make our own enum for this like we did for 16 | Link, but we want our users to be able to understand what the heck our return 17 | type is, and Option is so ubiquitous that *everyone* knows it. In fact, it's so 18 | fundamental that it's implicitly imported into scope in every file, as well 19 | as its variants `Some` and `None` (so we don't have to say `Option::None`). 20 | 21 | The pointy bits on `Option` indicate that Option is actually *generic* over 22 | T. That means that you can make an Option for *any* type! 23 | 24 | So uh, we have this `Link` thing, how do we figure out if it's Empty or has 25 | More? Pattern matching with `match`! 26 | 27 | ```rust ,ignore 28 | pub fn pop(&mut self) -> Option { 29 | match self.head { 30 | Link::Empty => { 31 | // TODO 32 | } 33 | Link::More(node) => { 34 | // TODO 35 | } 36 | }; 37 | } 38 | ``` 39 | 40 | ```text 41 | > cargo build 42 | 43 | error[E0308]: mismatched types 44 | --> src/first.rs:27:30 45 | | 46 | 27 | pub fn pop(&mut self) -> Option { 47 | | --- ^^^^^^^^^^^ expected enum `std::option::Option`, found () 48 | | | 49 | | this function's body doesn't return 50 | | 51 | = note: expected type `std::option::Option` 52 | found type `()` 53 | ``` 54 | 55 | Whoops, `pop` has to return a value, and we're not doing that yet. We *could* 56 | return `None`, but in this case it's probably a better idea to return 57 | `unimplemented!()`, to indicate that we aren't done implementing the function. 58 | `unimplemented!()` is a macro (`!` indicates a macro) that panics the program 59 | when we get to it (\~crashes it in a controlled manner). 60 | 61 | ```rust ,ignore 62 | pub fn pop(&mut self) -> Option { 63 | match self.head { 64 | Link::Empty => { 65 | // TODO 66 | } 67 | Link::More(node) => { 68 | // TODO 69 | } 70 | }; 71 | unimplemented!() 72 | } 73 | ``` 74 | 75 | Unconditional panics are an example of a [diverging function][diverging]. 76 | Diverging functions never return to the caller, so they may be used in places 77 | where a value of any type is expected. Here, `unimplemented!()` is being 78 | used in place of a value of type `Option`. 79 | 80 | Note also that we don't need to write `return` in our program. The last 81 | expression (basically line) in a function is implicitly its return value. This 82 | lets us express really simple things a bit more concisely. You can always 83 | explicitly return early with `return` like any other C-like language. 84 | 85 | ```text 86 | > cargo build 87 | 88 | error[E0507]: cannot move out of borrowed content 89 | --> src/first.rs:28:15 90 | | 91 | 28 | match self.head { 92 | | ^^^^^^^^^ 93 | | | 94 | | cannot move out of borrowed content 95 | | help: consider borrowing here: `&self.head` 96 | ... 97 | 32 | Link::More(node) => { 98 | | ---- data moved here 99 | | 100 | note: move occurs because `node` has type `std::boxed::Box`, which does not implement the `Copy` trait 101 | --> src/first.rs:32:24 102 | | 103 | 32 | Link::More(node) => { 104 | | ^^^^ 105 | ``` 106 | 107 | Come on Rust, get off our back! As always, Rust is hella mad at us. Thankfully, 108 | this time it's also giving us the full scoop! By default, a pattern match will 109 | try to move its contents into the new branch, but we can't do this because we 110 | don't own self by-value here. 111 | 112 | ```text 113 | help: consider borrowing here: `&self.head` 114 | ``` 115 | 116 | Rust says we should add a reference to our `match` to fix that. 🤷‍♀️ Let's try it: 117 | 118 | ```rust ,ignore 119 | pub fn pop(&mut self) -> Option { 120 | match &self.head { 121 | Link::Empty => { 122 | // TODO 123 | } 124 | Link::More(node) => { 125 | // TODO 126 | } 127 | }; 128 | unimplemented!() 129 | } 130 | ``` 131 | 132 | ```text 133 | > cargo build 134 | 135 | warning: unused variable: `node` 136 | --> src/first.rs:32:24 137 | | 138 | 32 | Link::More(node) => { 139 | | ^^^^ help: consider prefixing with an underscore: `_node` 140 | | 141 | = note: #[warn(unused_variables)] on by default 142 | 143 | warning: field is never used: `elem` 144 | --> src/first.rs:13:5 145 | | 146 | 13 | elem: i32, 147 | | ^^^^^^^^^ 148 | | 149 | = note: #[warn(dead_code)] on by default 150 | 151 | warning: field is never used: `next` 152 | --> src/first.rs:14:5 153 | | 154 | 14 | next: Link, 155 | | ^^^^^^^^^^ 156 | ``` 157 | 158 | Hooray, compiling again! Now let's figure out that logic. We want to make an 159 | Option, so let's make a variable for that. In the Empty case we need to return 160 | None. In the More case we need to return `Some(i32)`, and change the head of 161 | the list. So, let's try to do basically that? 162 | 163 | ```rust ,ignore 164 | pub fn pop(&mut self) -> Option { 165 | let result; 166 | match &self.head { 167 | Link::Empty => { 168 | result = None; 169 | } 170 | Link::More(node) => { 171 | result = Some(node.elem); 172 | self.head = node.next; 173 | } 174 | }; 175 | result 176 | } 177 | ``` 178 | 179 | ```text 180 | > cargo build 181 | Compiling lists v0.1.0 (/Users/ADesires/dev/temp/lists) 182 | error[E0507]: cannot move out of borrowed content 183 | --> src/first.rs:35:29 184 | | 185 | 35 | self.head = node.next; 186 | | ^^^^^^^^^ cannot move out of borrowed content 187 | 188 | ``` 189 | 190 | *head* 191 | 192 | *desk* 193 | 194 | We're trying to move out of `node` when all we have is a shared reference to it. 195 | 196 | We should probably step back and think about what we're trying to do. We want 197 | to: 198 | 199 | * Check if the list is empty. 200 | * If it's empty, just return None 201 | * If it's *not* empty 202 | * remove the head of the list 203 | * remove its `elem` 204 | * replace the list's head with its `next` 205 | * return `Some(elem)` 206 | 207 | The key insight is we want to *remove* things, which means we want to get the 208 | head of the list *by value*. We certainly can't do that through the shared 209 | reference we get through `&self.head`. We also "only" have a mutable reference 210 | to `self`, so the only way we can move stuff is to *replace it*. Looks like we're doing 211 | the Empty dance again! 212 | 213 | Let's try that: 214 | 215 | 216 | ```rust ,ignore 217 | pub fn pop(&mut self) -> Option { 218 | let result; 219 | match mem::replace(&mut self.head, Link::Empty) { 220 | Link::Empty => { 221 | result = None; 222 | } 223 | Link::More(node) => { 224 | result = Some(node.elem); 225 | self.head = node.next; 226 | } 227 | }; 228 | result 229 | } 230 | ``` 231 | 232 | ```text 233 | cargo build 234 | 235 | Finished dev [unoptimized + debuginfo] target(s) in 0.22s 236 | ``` 237 | 238 | O M G 239 | 240 | It compiled without *any* warnings!!!!! 241 | 242 | Actually I'm going to apply my own personal lint here: we made this `result` 243 | value to return, but actually we didn't need to do that at all! Just as a 244 | function evaluates to its last expression, every block also evaluates to 245 | its last expression. Normally we supress this behaviour with semi-colons, 246 | which instead makes the block evaluate to the empty tuple, `()`. This is 247 | actually the value that functions which don't declare a return value -- like 248 | `push` -- return. 249 | 250 | So instead, we can write `pop` as: 251 | 252 | ```rust ,ignore 253 | pub fn pop(&mut self) -> Option { 254 | match mem::replace(&mut self.head, Link::Empty) { 255 | Link::Empty => None, 256 | Link::More(node) => { 257 | self.head = node.next; 258 | Some(node.elem) 259 | } 260 | } 261 | } 262 | ``` 263 | 264 | Which is a bit more concise and idiomatic. Note that the Link::Empty branch 265 | completely lost its braces, because we only have one expression to 266 | evaluate. Just a nice shorthand for simple cases. 267 | 268 | ```text 269 | cargo build 270 | 271 | Finished dev [unoptimized + debuginfo] target(s) in 0.22s 272 | ``` 273 | 274 | Nice, still works! 275 | 276 | 277 | 278 | [ownership]: first-ownership.html 279 | [diverging]: https://doc.rust-lang.org/nightly/book/ch19-04-advanced-types.html#the-never-type-that-never-returns 280 | -------------------------------------------------------------------------------- /src/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 ,ignore 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 | error[E0507]: cannot move out of borrowed content 41 | --> src/first.rs:19:19 42 | | 43 | 19 | next: self.head, 44 | | ^^^^^^^^^ cannot move out of borrowed content 45 | ``` 46 | 47 | Nooooope. Rust is telling us the right thing, but it's certainly not obvious 48 | what exactly it means, or what to do about it: 49 | 50 | > cannot move out of borrowed content 51 | 52 | We're trying to move the `self.head` field out to `next`, but Rust doesn't want 53 | us doing that. This would leave `self` only partially initialized when we end 54 | the borrow and "give it back" to its rightful owner. As we said before, that's 55 | the *one* thing you can't do with an `&mut`: It would be super rude, 56 | and Rust is very polite (it would also be incredibly dangerous, but surely 57 | *that* isn't why it cares). 58 | 59 | What if we put something back? Namely, the node that we're creating: 60 | 61 | 62 | ```rust ,ignore 63 | pub fn push(&mut self, elem: i32) { 64 | let new_node = Box::new(Node { 65 | elem: elem, 66 | next: self.head, 67 | }); 68 | 69 | self.head = Link::More(new_node); 70 | } 71 | ``` 72 | 73 | ```text 74 | > cargo build 75 | error[E0507]: cannot move out of borrowed content 76 | --> src/first.rs:19:19 77 | | 78 | 19 | next: self.head, 79 | | ^^^^^^^^^ cannot move out of borrowed content 80 | ``` 81 | 82 | No dice. In principle, this is something Rust could actually accept, but it 83 | won't (for various reasons -- the most serious being [exception safety][]). We need 84 | some way to get the head without Rust noticing that it's gone. For advice, we 85 | turn to infamous Rust Hacker Indiana Jones: 86 | 87 | ![Indy Prepares to mem::replace](img/indy.gif) 88 | 89 | Ah yes, Indy suggests the `mem::replace` maneuver. This incredibly useful 90 | function lets us steal a value out of a borrow by *replacing* it with another 91 | value. Let's just pull in `std::mem` at the top of the file, so that `mem` is in 92 | local scope: 93 | 94 | ```rust ,ignore 95 | use std::mem; 96 | ``` 97 | 98 | and use it appropriately: 99 | 100 | ```rust ,ignore 101 | pub fn push(&mut self, elem: i32) { 102 | let new_node = Box::new(Node { 103 | elem: elem, 104 | next: mem::replace(&mut self.head, Link::Empty), 105 | }); 106 | 107 | self.head = Link::More(new_node); 108 | } 109 | ``` 110 | 111 | Here we `replace` self.head temporarily with Link::Empty before replacing it 112 | with the new head of the list. I'm not gonna lie: this is a pretty unfortunate 113 | thing to have to do. Sadly, we must (for now). 114 | 115 | But hey, that's `push` all done! Probably. We should probably test it, honestly. 116 | Right now the easiest way to do that is probably to write `pop`, and make sure 117 | that it produces the right results. 118 | 119 | 120 | 121 | 122 | 123 | [exception safety]: https://doc.rust-lang.org/nightly/nomicon/exception-safety.html 124 | -------------------------------------------------------------------------------- /src/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 it's 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 ,ignore 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 (/Users/ADesires/dev/temp/lists) 31 | Finished dev [unoptimized + debuginfo] target(s) in 1.00s 32 | Running /Users/ADesires/dev/lists/target/debug/deps/lists-86544f1d97438f1f 33 | 34 | running 1 test 35 | test first::test::basics ... ok 36 | 37 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 38 | ; 0 filtered out 39 | ``` 40 | 41 | Yay our do-nothing test passed! Let's make it not-do-nothing. We'll do that 42 | with the `assert_eq!` macro. This isn't some special testing magic. All it 43 | does is compare the two things you give it, and panic the program if they don't 44 | match. Yep, you indicate failure to the test harness by freaking out! 45 | 46 | ```rust ,ignore 47 | mod test { 48 | #[test] 49 | fn basics() { 50 | let mut list = List::new(); 51 | 52 | // Check empty list behaves right 53 | assert_eq!(list.pop(), None); 54 | 55 | // Populate list 56 | list.push(1); 57 | list.push(2); 58 | list.push(3); 59 | 60 | // Check normal removal 61 | assert_eq!(list.pop(), Some(3)); 62 | assert_eq!(list.pop(), Some(2)); 63 | 64 | // Push some more just to make sure nothing's corrupted 65 | list.push(4); 66 | list.push(5); 67 | 68 | // Check normal removal 69 | assert_eq!(list.pop(), Some(5)); 70 | assert_eq!(list.pop(), Some(4)); 71 | 72 | // Check exhaustion 73 | assert_eq!(list.pop(), Some(1)); 74 | assert_eq!(list.pop(), None); 75 | } 76 | } 77 | ``` 78 | 79 | ```text 80 | > cargo test 81 | 82 | error[E0433]: failed to resolve: use of undeclared type or module `List` 83 | --> src/first.rs:43:24 84 | | 85 | 43 | let mut list = List::new(); 86 | | ^^^^ use of undeclared type or module `List` 87 | 88 | 89 | ``` 90 | 91 | Oops! Because we made a new module, we need to pull in List explicitly to use 92 | it. 93 | 94 | ```rust ,ignore 95 | mod test { 96 | use super::List; 97 | // everything else the same 98 | } 99 | ``` 100 | 101 | ```text 102 | > cargo test 103 | 104 | warning: unused import: `super::List` 105 | --> src/first.rs:45:9 106 | | 107 | 45 | use super::List; 108 | | ^^^^^^^^^^^ 109 | | 110 | = note: #[warn(unused_imports)] on by default 111 | 112 | Finished dev [unoptimized + debuginfo] target(s) in 0.43s 113 | Running /Users/ADesires/dev/lists/target/debug/deps/lists-86544f1d97438f1f 114 | 115 | running 1 test 116 | test first::test::basics ... ok 117 | 118 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 119 | ; 0 filtered out 120 | ``` 121 | 122 | Yay! 123 | 124 | What's up with that warning though...? We clearly use List in our test! 125 | 126 | ...but only when testing! To appease the compiler (and to be friendly to our 127 | consumers), we should indicate that the whole `test` module should only be 128 | compiled if we're running tests. 129 | 130 | 131 | ```rust ,ignore 132 | #[cfg(test)] 133 | mod test { 134 | use super::List; 135 | // everything else the same 136 | } 137 | ``` 138 | 139 | And that's everything for testing! 140 | -------------------------------------------------------------------------------- /src/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 ,ignore 12 | // in lib.rs 13 | pub mod first; 14 | ``` 15 | 16 | -------------------------------------------------------------------------------- /src/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 ,ignore 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 | 31 | error[E0609]: no field `elem` on type `std::rc::Rc>>` 32 | --> src/fourth.rs:64:22 33 | | 34 | 64 | old_head.elem 35 | | ^^^^ unknown field 36 | ``` 37 | 38 | ACK. *RefCells*. Gotta `borrow_mut` again I guess... 39 | 40 | ```rust ,ignore 41 | pub fn pop_front(&mut self) -> Option { 42 | self.head.take().map(|old_head| { 43 | match old_head.borrow_mut().next.take() { 44 | Some(new_head) => { 45 | new_head.borrow_mut().prev.take(); 46 | self.head = Some(new_head); 47 | } 48 | None => { 49 | self.tail.take(); 50 | } 51 | } 52 | old_head.borrow_mut().elem 53 | }) 54 | } 55 | ``` 56 | 57 | ```text 58 | cargo build 59 | 60 | error[E0507]: cannot move out of borrowed content 61 | --> src/fourth.rs:64:13 62 | | 63 | 64 | old_head.borrow_mut().elem 64 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content 65 | ``` 66 | 67 | *sigh* 68 | 69 | > cannot move out of borrowed content 70 | 71 | Hrm... It seems that Box was *really* spoiling us. `borrow_mut` only gets us 72 | an `&mut Node`, but we can't move out of that! 73 | 74 | We need something that takes a `RefCell` and gives us a `T`. Let's check 75 | [the docs][refcell] for something like that: 76 | 77 | > `fn into_inner(self) -> T` 78 | > 79 | > Consumes the RefCell, returning the wrapped value. 80 | 81 | That looks promising! 82 | 83 | ```rust ,ignore 84 | old_head.into_inner().elem 85 | ``` 86 | 87 | ```text 88 | > cargo build 89 | 90 | error[E0507]: cannot move out of an `Rc` 91 | --> src/fourth.rs:64:13 92 | | 93 | 64 | old_head.into_inner().elem 94 | | ^^^^^^^^ cannot move out of an `Rc` 95 | ``` 96 | 97 | Ah dang. `into_inner` wants to move out the RefCell, but we can't, because it's 98 | in an `Rc`. As we saw in the previous chapter, `Rc` only lets us get shared references 99 | into its internals. That makes sense, because that's *the whole point* of 100 | reference counted pointers: they're shared! 101 | 102 | This was a problem for us when we wanted to implement Drop for our reference 103 | counted list, and the solution is the same: `Rc::try_unwrap`, which moves out 104 | the contents of an Rc if its refcount is 1. 105 | 106 | ```rust ,ignore 107 | Rc::try_unwrap(old_head).unwrap().into_inner().elem 108 | ``` 109 | 110 | `Rc::try_unwrap` returns a `Result>`. Results are basically a 111 | generalized `Option`, where the `None` case has data associated with it. In 112 | this case, the `Rc` you tried to unwrap. Since we don't care about the case 113 | where it fails (if we wrote our program correctly, it *has* to succeed), we 114 | just call `unwrap` on it. 115 | 116 | Anyway, let's see what compiler error we get next (let's face it, there's going 117 | to be one). 118 | 119 | ```text 120 | > cargo build 121 | 122 | error[E0599]: no method named `unwrap` found for type `std::result::Result>, std::rc::Rc>>>` in the current scope 123 | --> src/fourth.rs:64:38 124 | | 125 | 64 | Rc::try_unwrap(old_head).unwrap().into_inner().elem 126 | | ^^^^^^ 127 | | 128 | = note: the method `unwrap` exists but the following trait bounds were not satisfied: 129 | `std::rc::Rc>> : std::fmt::Debug` 130 | ``` 131 | 132 | UGH. `unwrap` on Result requires that you can debug-print the error case. 133 | `RefCell` only implements `Debug` if `T` does. `Node` doesn't implement Debug. 134 | 135 | Rather than doing that, let's just work around it by converting the Result to 136 | an Option with `ok`: 137 | 138 | ```rust ,ignore 139 | Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem 140 | ``` 141 | 142 | PLEASE. 143 | 144 | ```text 145 | cargo build 146 | 147 | ``` 148 | 149 | YES. 150 | 151 | *phew* 152 | 153 | We did it. 154 | 155 | We implemented `push` and `pop`. 156 | 157 | Let's test by stealing the old `stack` basic test (because that's all that 158 | we've implemented so far): 159 | 160 | ```rust ,ignore 161 | #[cfg(test)] 162 | mod test { 163 | use super::List; 164 | 165 | #[test] 166 | fn basics() { 167 | let mut list = List::new(); 168 | 169 | // Check empty list behaves right 170 | assert_eq!(list.pop_front(), None); 171 | 172 | // Populate list 173 | list.push_front(1); 174 | list.push_front(2); 175 | list.push_front(3); 176 | 177 | // Check normal removal 178 | assert_eq!(list.pop_front(), Some(3)); 179 | assert_eq!(list.pop_front(), Some(2)); 180 | 181 | // Push some more just to make sure nothing's corrupted 182 | list.push_front(4); 183 | list.push_front(5); 184 | 185 | // Check normal removal 186 | assert_eq!(list.pop_front(), Some(5)); 187 | assert_eq!(list.pop_front(), Some(4)); 188 | 189 | // Check exhaustion 190 | assert_eq!(list.pop_front(), Some(1)); 191 | assert_eq!(list.pop_front(), None); 192 | } 193 | } 194 | ``` 195 | 196 | ```text 197 | cargo test 198 | 199 | Running target/debug/lists-5c71138492ad4b4a 200 | 201 | running 9 tests 202 | test first::test::basics ... ok 203 | test fourth::test::basics ... ok 204 | test second::test::iter_mut ... ok 205 | test second::test::basics ... ok 206 | test fifth::test::iter_mut ... ok 207 | test third::test::basics ... ok 208 | test second::test::iter ... ok 209 | test third::test::iter ... ok 210 | test second::test::into_iter ... ok 211 | 212 | test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured 213 | 214 | ``` 215 | 216 | *Nailed it*. 217 | 218 | Now that we can properly remove things from the list, we can implement Drop. 219 | Drop is a little more conceptually interesting this time around. Where 220 | previously we bothered to implement Drop for our stacks just to avoid unbounded 221 | recursion, now we need to implement Drop to get *anything* to happen at all. 222 | 223 | `Rc` can't deal with cycles. If there's a cycle, everything will keep everything 224 | else alive. A doubly-linked list, as it turns out, is just a big chain of tiny 225 | cycles! So when we drop our list, the two end nodes will have their refcounts 226 | decremented down to 1... and then nothing else will happen. Well, if our list 227 | contains exactly one node we're good to go. But ideally a list should work right 228 | if it contains multiple elements. Maybe that's just me. 229 | 230 | As we saw, removing elements was a bit painful. So the easiest thing for us to 231 | do is just `pop` until we get None: 232 | 233 | ```rust ,ignore 234 | impl Drop for List { 235 | fn drop(&mut self) { 236 | while self.pop_front().is_some() {} 237 | } 238 | } 239 | ``` 240 | 241 | ```text 242 | cargo build 243 | 244 | ``` 245 | 246 | (We actually could have done this with our mutable stacks, but shortcuts are for 247 | people who understand things!) 248 | 249 | We could look at implementing the `_back` versions of `push` and `pop`, but 250 | they're just copy-paste jobs which we'll defer to later in the chapter. For now 251 | let's look at more interesting things! 252 | 253 | 254 | [refcell]: https://doc.rust-lang.org/std/cell/struct.RefCell.html 255 | [multirust]: https://github.com/brson/multirust 256 | [downloads]: https://www.rust-lang.org/install.html 257 | -------------------------------------------------------------------------------- /src/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 ,ignore 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 significantly 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 ,ignore 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 | 76 | error[E0609]: no field `prev` on type `std::rc::Rc>>` 77 | --> src/fourth.rs:39:26 78 | | 79 | 39 | old_head.prev = Some(new_head.clone()); // +1 new_head 80 | | ^^^^ unknown field 81 | 82 | error[E0609]: no field `next` on type `std::rc::Rc>>` 83 | --> src/fourth.rs:40:26 84 | | 85 | 40 | new_head.next = Some(old_head); // +1 old_head 86 | | ^^^^ unknown field 87 | ``` 88 | 89 | Alright. Compiler error. Good start. Good start. 90 | 91 | Why can't we access the `prev` and `next` fields on our nodes? It worked before 92 | when we just had an `Rc`. Seems like the `RefCell` is getting in the way. 93 | 94 | We should probably check the docs. 95 | 96 | *Google's "rust refcell"* 97 | 98 | *[clicks first link](https://doc.rust-lang.org/std/cell/struct.RefCell.html)* 99 | 100 | > A mutable memory location with dynamically checked borrow rules 101 | > 102 | > See the [module-level documentation](https://doc.rust-lang.org/std/cell/index.html) for more. 103 | 104 | *clicks link* 105 | 106 | > Shareable mutable containers. 107 | > 108 | > Values of the `Cell` and `RefCell` types may be mutated through shared references (i.e. 109 | > the common `&T` type), whereas most Rust types can only be mutated through unique (`&mut T`) 110 | > references. We say that `Cell` and `RefCell` provide 'interior mutability', in contrast 111 | > with typical Rust types that exhibit 'inherited mutability'. 112 | > 113 | > Cell types come in two flavors: `Cell` and `RefCell`. `Cell` provides `get` and `set` 114 | > methods that change the interior value with a single method call. `Cell` though is only 115 | > compatible with types that implement `Copy`. For other types, one must use the `RefCell` 116 | > type, acquiring a write lock before mutating. 117 | > 118 | > `RefCell` uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can 119 | > claim temporary, exclusive, mutable access to the inner value. Borrows for `RefCell`s are 120 | > tracked 'at runtime', unlike Rust's native reference types which are entirely tracked 121 | > statically, at compile time. Because `RefCell` borrows are dynamic it is possible to attempt 122 | > to borrow a value that is already mutably borrowed; when this happens it results in thread 123 | > panic. 124 | > 125 | > # When to choose interior mutability 126 | > 127 | > The more common inherited mutability, where one must have unique access to mutate a value, is 128 | > one of the key language elements that enables Rust to reason strongly about pointer aliasing, 129 | > statically preventing crash bugs. Because of that, inherited mutability is preferred, and 130 | > interior mutability is something of a last resort. Since cell types enable mutation where it 131 | > would otherwise be disallowed though, there are occasions when interior mutability might be 132 | > appropriate, or even *must* be used, e.g. 133 | > 134 | > * Introducing inherited mutability roots to shared types. 135 | > * Implementation details of logically-immutable methods. 136 | > * Mutating implementations of `Clone`. 137 | > 138 | > ## Introducing inherited mutability roots to shared types 139 | > 140 | > Shared smart pointer types, including `Rc` and `Arc`, provide containers that can be 141 | > cloned and shared between multiple parties. Because the contained values may be 142 | > multiply-aliased, they can only be borrowed as shared references, not mutable references. 143 | > Without cells it would be impossible to mutate data inside of shared boxes at all! 144 | > 145 | > It's very common then to put a `RefCell` inside shared pointer types to reintroduce 146 | > mutability: 147 | > 148 | > ```rust ,ignore 149 | > use std::collections::HashMap; 150 | > use std::cell::RefCell; 151 | > use std::rc::Rc; 152 | > 153 | > fn main() { 154 | > let shared_map: Rc> = Rc::new(RefCell::new(HashMap::new())); 155 | > shared_map.borrow_mut().insert("africa", 92388); 156 | > shared_map.borrow_mut().insert("kyoto", 11837); 157 | > shared_map.borrow_mut().insert("piccadilly", 11826); 158 | > shared_map.borrow_mut().insert("marbles", 38); 159 | > } 160 | > ``` 161 | > 162 | > Note that this example uses `Rc` and not `Arc`. `RefCell`s are for single-threaded 163 | > scenarios. Consider using `Mutex` if you need shared mutability in a multi-threaded 164 | > situation. 165 | 166 | Hey, Rust's docs continue to be incredibly awesome. 167 | 168 | The meaty bit we care about is this line: 169 | 170 | ```rust ,ignore 171 | shared_map.borrow_mut().insert("africa", 92388); 172 | ``` 173 | 174 | In particular, the `borrow_mut` thing. Seems we need to explicitly borrow a 175 | RefCell. The `.` operator's not going to do it for us. Weird. Let's try: 176 | 177 | ```rust ,ignore 178 | pub fn push_front(&mut self, elem: T) { 179 | let new_head = Node::new(elem); 180 | match self.head.take() { 181 | Some(old_head) => { 182 | old_head.borrow_mut().prev = Some(new_head.clone()); 183 | new_head.borrow_mut().next = Some(old_head); 184 | self.head = Some(new_head); 185 | } 186 | None => { 187 | self.tail = Some(new_head.clone()); 188 | self.head = Some(new_head); 189 | } 190 | } 191 | } 192 | ``` 193 | 194 | 195 | ```text 196 | > cargo build 197 | 198 | warning: field is never used: `elem` 199 | --> src/fourth.rs:12:5 200 | | 201 | 12 | elem: T, 202 | | ^^^^^^^ 203 | | 204 | = note: #[warn(dead_code)] on by default 205 | ``` 206 | 207 | Hey, it built! Docs win again. 208 | -------------------------------------------------------------------------------- /src/fourth-final.md: -------------------------------------------------------------------------------- 1 | # Final Code 2 | 3 | Alright, so that's implementing a 100% safe doubly-linked list in Rust. It was 4 | a nightmare to implement, leaks implementation details, and doesn't support several 5 | fundamental operations. 6 | 7 | But, it exists. 8 | 9 | Oh, I guess it's also riddled with tons of "unnecessary" runtime checks for 10 | correctness between `Rc` and `RefCell`. I put unnecessary in quotes because 11 | they're actually necessary to guarantee the whole *actually being safe* thing. 12 | We encountered a few places where those checks actually *were* necessary. 13 | Doubly-linked lists have a horribly tangled aliasing and ownership story! 14 | 15 | Still, it's a thing we can do. Especially if we don't care about exposing 16 | internal data structures to our consumers. 17 | 18 | From here on out, we're going to be focusing on other side of this coin: 19 | getting back all the control by making our implementation *unsafe*. 20 | 21 | ```rust 22 | use std::rc::Rc; 23 | use std::cell::{Ref, RefMut, RefCell}; 24 | 25 | pub struct List { 26 | head: Link, 27 | tail: Link, 28 | } 29 | 30 | type Link = Option>>>; 31 | 32 | struct Node { 33 | elem: T, 34 | next: Link, 35 | prev: Link, 36 | } 37 | 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_back(&mut self) -> Option { 85 | self.tail.take().map(|old_tail| { 86 | match old_tail.borrow_mut().prev.take() { 87 | Some(new_tail) => { 88 | new_tail.borrow_mut().next.take(); 89 | self.tail = Some(new_tail); 90 | } 91 | None => { 92 | self.head.take(); 93 | } 94 | } 95 | Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem 96 | }) 97 | } 98 | 99 | pub fn pop_front(&mut self) -> Option { 100 | self.head.take().map(|old_head| { 101 | match old_head.borrow_mut().next.take() { 102 | Some(new_head) => { 103 | new_head.borrow_mut().prev.take(); 104 | self.head = Some(new_head); 105 | } 106 | None => { 107 | self.tail.take(); 108 | } 109 | } 110 | Rc::try_unwrap(old_head).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_back(&self) -> Option> { 121 | self.tail.as_ref().map(|node| { 122 | Ref::map(node.borrow(), |node| &node.elem) 123 | }) 124 | } 125 | 126 | pub fn peek_back_mut(&mut self) -> Option> { 127 | self.tail.as_ref().map(|node| { 128 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 129 | }) 130 | } 131 | 132 | pub fn peek_front_mut(&mut self) -> Option> { 133 | self.head.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 | pub struct IntoIter(List); 150 | 151 | impl Iterator for IntoIter { 152 | type Item = T; 153 | 154 | fn next(&mut self) -> Option { 155 | self.0.pop_front() 156 | } 157 | } 158 | 159 | impl DoubleEndedIterator for IntoIter { 160 | fn next_back(&mut self) -> Option { 161 | self.0.pop_back() 162 | } 163 | } 164 | 165 | #[cfg(test)] 166 | mod test { 167 | use super::List; 168 | 169 | #[test] 170 | fn basics() { 171 | let mut list = List::new(); 172 | 173 | // Check empty list behaves right 174 | assert_eq!(list.pop_front(), None); 175 | 176 | // Populate list 177 | list.push_front(1); 178 | list.push_front(2); 179 | list.push_front(3); 180 | 181 | // Check normal removal 182 | assert_eq!(list.pop_front(), Some(3)); 183 | assert_eq!(list.pop_front(), Some(2)); 184 | 185 | // Push some more just to make sure nothing's corrupted 186 | list.push_front(4); 187 | list.push_front(5); 188 | 189 | // Check normal removal 190 | assert_eq!(list.pop_front(), Some(5)); 191 | assert_eq!(list.pop_front(), Some(4)); 192 | 193 | // Check exhaustion 194 | assert_eq!(list.pop_front(), Some(1)); 195 | assert_eq!(list.pop_front(), None); 196 | 197 | // ---- back ----- 198 | 199 | // Check empty list behaves right 200 | assert_eq!(list.pop_back(), None); 201 | 202 | // Populate list 203 | list.push_back(1); 204 | list.push_back(2); 205 | list.push_back(3); 206 | 207 | // Check normal removal 208 | assert_eq!(list.pop_back(), Some(3)); 209 | assert_eq!(list.pop_back(), Some(2)); 210 | 211 | // Push some more just to make sure nothing's corrupted 212 | list.push_back(4); 213 | list.push_back(5); 214 | 215 | // Check normal removal 216 | assert_eq!(list.pop_back(), Some(5)); 217 | assert_eq!(list.pop_back(), Some(4)); 218 | 219 | // Check exhaustion 220 | assert_eq!(list.pop_back(), Some(1)); 221 | assert_eq!(list.pop_back(), None); 222 | } 223 | 224 | #[test] 225 | fn peek() { 226 | let mut list = List::new(); 227 | assert!(list.peek_front().is_none()); 228 | assert!(list.peek_back().is_none()); 229 | assert!(list.peek_front_mut().is_none()); 230 | assert!(list.peek_back_mut().is_none()); 231 | 232 | list.push_front(1); list.push_front(2); list.push_front(3); 233 | 234 | assert_eq!(&*list.peek_front().unwrap(), &3); 235 | assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); 236 | assert_eq!(&*list.peek_back().unwrap(), &1); 237 | assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); 238 | } 239 | 240 | #[test] 241 | fn into_iter() { 242 | let mut list = List::new(); 243 | list.push_front(1); list.push_front(2); list.push_front(3); 244 | 245 | let mut iter = list.into_iter(); 246 | assert_eq!(iter.next(), Some(3)); 247 | assert_eq!(iter.next_back(), Some(1)); 248 | assert_eq!(iter.next(), Some(2)); 249 | assert_eq!(iter.next_back(), None); 250 | assert_eq!(iter.next(), None); 251 | } 252 | } 253 | ``` 254 | -------------------------------------------------------------------------------- /src/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 ,ignore 7 | fn borrow(&self) -> Ref<'_, T>; 8 | fn borrow_mut(&self) -> RefMut<'_, 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 also 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 ,ignore 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 | 52 | warning: field is never used: `head` 53 | --> src/fourth.rs:5:5 54 | | 55 | 5 | head: Link, 56 | | ^^^^^^^^^^^^^ 57 | | 58 | = note: #[warn(dead_code)] on by default 59 | 60 | warning: field is never used: `tail` 61 | --> src/fourth.rs:6:5 62 | | 63 | 6 | tail: Link, 64 | | ^^^^^^^^^^^^^ 65 | 66 | warning: field is never used: `elem` 67 | --> src/fourth.rs:12:5 68 | | 69 | 12 | elem: T, 70 | | ^^^^^^^ 71 | 72 | warning: field is never used: `next` 73 | --> src/fourth.rs:13:5 74 | | 75 | 13 | next: Link, 76 | | ^^^^^^^^^^^^^ 77 | 78 | warning: field is never used: `prev` 79 | --> src/fourth.rs:14:5 80 | | 81 | 14 | prev: Link, 82 | | ^^^^^^^^^^^^^ 83 | ``` 84 | 85 | Hey, it built! Lots of dead code warnings, but it built! Let's try to use it. 86 | -------------------------------------------------------------------------------- /src/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 ,ignore 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 ,ignore 24 | pub fn peek_front(&self) -> Option<&T> { 25 | self.head.as_ref().map(|node| { 26 | // BORROW!!!! 27 | &node.borrow().elem 28 | }) 29 | } 30 | ``` 31 | 32 | HAH. 33 | 34 | ```text 35 | cargo build 36 | 37 | error[E0515]: cannot return value referencing temporary value 38 | --> src/fourth.rs:66:13 39 | | 40 | 66 | &node.borrow().elem 41 | | ^ ----------^^^^^ 42 | | | | 43 | | | temporary value created here 44 | | | 45 | | returns a value referencing data owned by the current function 46 | ``` 47 | 48 | Ok I'm just burning my computer. 49 | 50 | This is exactly the same logic as our singly-linked stack. Why are things 51 | different. WHY. 52 | 53 | The answer is really the whole moral of this chapter: RefCells make everything 54 | sadness. Up until now, RefCells have just been a nuisance. Now they're going to 55 | become a nightmare. 56 | 57 | So what's going on? To understand that, we need to go back to the definition of 58 | `borrow`: 59 | 60 | ```rust ,ignore 61 | fn borrow<'a>(&'a self) -> Ref<'a, T> 62 | fn borrow_mut<'a>(&'a self) -> RefMut<'a, T> 63 | ``` 64 | 65 | In the layout section we said: 66 | 67 | > Rather than enforcing this statically, RefCell enforces them at runtime. 68 | > If you break the rules, RefCell will just panic and crash the program. 69 | > Why does it return these Ref and RefMut things? Well, they basically behave 70 | > like `Rc`s but for borrowing. Also they keep the RefCell borrowed until they go out 71 | > of scope. **We'll get to that later.** 72 | 73 | It's later. 74 | 75 | `Ref` and `RefMut` implement `Deref` and `DerefMut` respectively. So for most 76 | intents and purposes they behave *exactly* like `&T` and `&mut T`. However, 77 | because of how those traits work, the reference that's returned is connected 78 | to the lifetime of the Ref, and not the actual RefCell. This means that the Ref 79 | has to be sitting around as long as we keep the reference around. 80 | 81 | This is in fact necessary for correctness. When a Ref gets dropped, it tells 82 | the RefCell that it's not borrowed anymore. So if we *did* manage to hold onto our 83 | reference longer than the Ref existed, we could get a RefMut while a reference 84 | was kicking around and totally break Rust's type system in half. 85 | 86 | So where does that leave us? We only want to return a reference, but we need 87 | to keep this Ref thing around. But as soon as we return the reference from 88 | `peek`, the function is over and the `Ref` goes out of scope. 89 | 90 | 😖 91 | 92 | As far as I know, we're actually totally dead in the water here. You can't 93 | totally encapsulate the use of RefCells like that. 94 | 95 | But... what if we just give up on totally hiding our implementation details? 96 | What if we returns Refs? 97 | 98 | ```rust ,ignore 99 | pub fn peek_front(&self) -> Option> { 100 | self.head.as_ref().map(|node| { 101 | node.borrow() 102 | }) 103 | } 104 | ``` 105 | 106 | ```text 107 | > cargo build 108 | 109 | error[E0412]: cannot find type `Ref` in this scope 110 | --> src/fourth.rs:63:40 111 | | 112 | 63 | pub fn peek_front(&self) -> Option> { 113 | | ^^^ not found in this scope 114 | help: possible candidates are found in other modules, you can import them into scope 115 | | 116 | 1 | use core::cell::Ref; 117 | | 118 | 1 | use std::cell::Ref; 119 | | 120 | ``` 121 | 122 | Blurp. Gotta import some stuff. 123 | 124 | 125 | ```rust ,ignore 126 | use std::cell::{Ref, RefCell}; 127 | ``` 128 | 129 | ```text 130 | > cargo build 131 | 132 | error[E0308]: mismatched types 133 | --> src/fourth.rs:64:9 134 | | 135 | 64 | / self.head.as_ref().map(|node| { 136 | 65 | | node.borrow() 137 | 66 | | }) 138 | | |__________^ expected type parameter, found struct `fourth::Node` 139 | | 140 | = note: expected type `std::option::Option>` 141 | found type `std::option::Option>>` 142 | ``` 143 | 144 | Hmm... that's right. We have a `Ref>`, but we want a `Ref`. We could 145 | abandon all hope of encapsulation and just return that. We could also make 146 | things even more complicated and wrap `Ref>` in a new type to only 147 | expose access to an `&T`. 148 | 149 | Both of those options are *kinda* lame. 150 | 151 | Instead, we're going to go deeper down. Let's 152 | have some *fun*. Our source of fun is *this beast*: 153 | 154 | ```rust ,ignore 155 | map(orig: Ref<'b, T>, f: F) -> Ref<'b, U> 156 | where F: FnOnce(&T) -> &U, 157 | U: ?Sized 158 | ``` 159 | 160 | > Make a new Ref for a component of the borrowed data. 161 | 162 | Yes: just like you can map over an Option, you can map over a Ref. 163 | 164 | I'm sure someone somewhere is really excited because *monads* or whatever but 165 | I don't care about any of that. Also I don't think it's a proper monad since 166 | there's no None-like case, but I digress. 167 | 168 | It's cool and that's all that matters to me. *I need this*. 169 | 170 | ```rust ,ignore 171 | pub fn peek_front(&self) -> Option> { 172 | self.head.as_ref().map(|node| { 173 | Ref::map(node.borrow(), |node| &node.elem) 174 | }) 175 | } 176 | ``` 177 | 178 | ```text 179 | > cargo build 180 | ``` 181 | 182 | Awww yissss 183 | 184 | Let's make sure this is working by munging up the test from our stack. We need 185 | to do some munging to deal with the fact that Refs don't implement comparisons. 186 | 187 | ```rust ,ignore 188 | #[test] 189 | fn peek() { 190 | let mut list = List::new(); 191 | assert!(list.peek_front().is_none()); 192 | list.push_front(1); list.push_front(2); list.push_front(3); 193 | 194 | assert_eq!(&*list.peek_front().unwrap(), &3); 195 | } 196 | ``` 197 | 198 | 199 | ```text 200 | > cargo test 201 | 202 | Running target/debug/lists-5c71138492ad4b4a 203 | 204 | running 10 tests 205 | test first::test::basics ... ok 206 | test fourth::test::basics ... ok 207 | test second::test::basics ... ok 208 | test fourth::test::peek ... ok 209 | test second::test::iter_mut ... ok 210 | test second::test::into_iter ... ok 211 | test third::test::basics ... ok 212 | test second::test::peek ... ok 213 | test second::test::iter ... ok 214 | test third::test::iter ... ok 215 | 216 | test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured 217 | 218 | ``` 219 | 220 | Great! 221 | -------------------------------------------------------------------------------- /src/fourth-symmetry.md: -------------------------------------------------------------------------------- 1 | # Symmetric Junk 2 | 3 | Alright let's get all that combinatoric symmetry 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 ,ignore 16 | use std::cell::{Ref, RefCell, RefMut}; 17 | 18 | //.. 19 | 20 | pub fn push_back(&mut self, elem: T) { 21 | let new_tail = Node::new(elem); 22 | match self.tail.take() { 23 | Some(old_tail) => { 24 | old_tail.borrow_mut().next = Some(new_tail.clone()); 25 | new_tail.borrow_mut().prev = Some(old_tail); 26 | self.tail = Some(new_tail); 27 | } 28 | None => { 29 | self.head = Some(new_tail.clone()); 30 | self.tail = Some(new_tail); 31 | } 32 | } 33 | } 34 | 35 | pub fn pop_back(&mut self) -> Option { 36 | self.tail.take().map(|old_tail| { 37 | match old_tail.borrow_mut().prev.take() { 38 | Some(new_tail) => { 39 | new_tail.borrow_mut().next.take(); 40 | self.tail = Some(new_tail); 41 | } 42 | None => { 43 | self.head.take(); 44 | } 45 | } 46 | Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem 47 | }) 48 | } 49 | 50 | pub fn peek_back(&self) -> Option> { 51 | self.tail.as_ref().map(|node| { 52 | Ref::map(node.borrow(), |node| &node.elem) 53 | }) 54 | } 55 | 56 | pub fn peek_back_mut(&mut self) -> Option> { 57 | self.tail.as_ref().map(|node| { 58 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 59 | }) 60 | } 61 | 62 | pub fn peek_front_mut(&mut self) -> Option> { 63 | self.head.as_ref().map(|node| { 64 | RefMut::map(node.borrow_mut(), |node| &mut node.elem) 65 | }) 66 | } 67 | ``` 68 | 69 | And massively flesh out our tests: 70 | 71 | 72 | ```rust ,ignore 73 | #[test] 74 | fn basics() { 75 | let mut list = List::new(); 76 | 77 | // Check empty list behaves right 78 | assert_eq!(list.pop_front(), None); 79 | 80 | // Populate list 81 | list.push_front(1); 82 | list.push_front(2); 83 | list.push_front(3); 84 | 85 | // Check normal removal 86 | assert_eq!(list.pop_front(), Some(3)); 87 | assert_eq!(list.pop_front(), Some(2)); 88 | 89 | // Push some more just to make sure nothing's corrupted 90 | list.push_front(4); 91 | list.push_front(5); 92 | 93 | // Check normal removal 94 | assert_eq!(list.pop_front(), Some(5)); 95 | assert_eq!(list.pop_front(), Some(4)); 96 | 97 | // Check exhaustion 98 | assert_eq!(list.pop_front(), Some(1)); 99 | assert_eq!(list.pop_front(), None); 100 | 101 | // ---- back ----- 102 | 103 | // Check empty list behaves right 104 | assert_eq!(list.pop_back(), None); 105 | 106 | // Populate list 107 | list.push_back(1); 108 | list.push_back(2); 109 | list.push_back(3); 110 | 111 | // Check normal removal 112 | assert_eq!(list.pop_back(), Some(3)); 113 | assert_eq!(list.pop_back(), Some(2)); 114 | 115 | // Push some more just to make sure nothing's corrupted 116 | list.push_back(4); 117 | list.push_back(5); 118 | 119 | // Check normal removal 120 | assert_eq!(list.pop_back(), Some(5)); 121 | assert_eq!(list.pop_back(), Some(4)); 122 | 123 | // Check exhaustion 124 | assert_eq!(list.pop_back(), Some(1)); 125 | assert_eq!(list.pop_back(), None); 126 | } 127 | 128 | #[test] 129 | fn peek() { 130 | let mut list = List::new(); 131 | assert!(list.peek_front().is_none()); 132 | assert!(list.peek_back().is_none()); 133 | assert!(list.peek_front_mut().is_none()); 134 | assert!(list.peek_back_mut().is_none()); 135 | 136 | list.push_front(1); list.push_front(2); list.push_front(3); 137 | 138 | assert_eq!(&*list.peek_front().unwrap(), &3); 139 | assert_eq!(&mut *list.peek_front_mut().unwrap(), &mut 3); 140 | assert_eq!(&*list.peek_back().unwrap(), &1); 141 | assert_eq!(&mut *list.peek_back_mut().unwrap(), &mut 1); 142 | } 143 | ``` 144 | 145 | Are there some cases we're not testing? Probably. The combinatoric space 146 | has really blown up here. Our code is at very least not *obviously wrong*. 147 | 148 | ```text 149 | > cargo test 150 | 151 | Running target/debug/lists-5c71138492ad4b4a 152 | 153 | running 10 tests 154 | test first::test::basics ... ok 155 | test fourth::test::basics ... ok 156 | test second::test::basics ... ok 157 | test fourth::test::peek ... ok 158 | test second::test::iter ... ok 159 | test third::test::iter ... ok 160 | test second::test::into_iter ... ok 161 | test second::test::iter_mut ... ok 162 | test second::test::peek ... ok 163 | test third::test::basics ... ok 164 | 165 | test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured 166 | 167 | ``` 168 | 169 | Nice. Copy-pasting is the best kind of programming. 170 | -------------------------------------------------------------------------------- /src/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 | ```rust ,ignore 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 | Disclaimer: this chapter is basically a demonstration that this is a very bad idea. 26 | -------------------------------------------------------------------------------- /src/img/indy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-unofficial/too-many-lists/1b0b264d9a64d436aaed361a360cb738be88c4c6/src/img/indy.gif -------------------------------------------------------------------------------- /src/img/profbee.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-unofficial/too-many-lists/1b0b264d9a64d436aaed361a360cb738be88c4c6/src/img/profbee.gif -------------------------------------------------------------------------------- /src/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 | ```rust ,ignore 12 | // lib.rs 13 | // ... 14 | pub mod silly1; // NEW! 15 | ``` 16 | 17 | ```rust ,ignore 18 | // silly1.rs 19 | use crate::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 ,ignore 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 | ```rust ,ignore 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 ,ignore 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 ,ignore 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 ,ignore 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 ,ignore 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 | 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 | 233 | This is an extreme example of a *finger* data structure, where we maintain 234 | some kind of finger into the structure, and as a consequence can support 235 | operations on locations in time proportional to the distance from the finger. 236 | 237 | We can make very fast changes to the list around our finger, but if we want 238 | to make changes far away from our finger we have to walk all the way over there. 239 | We can permanently walk over there by shifting the elements from one stack to 240 | the other, or we could just walk along the links with an `&mut` 241 | temporarily to do the changes. However the `&mut` can never go back up the 242 | list, while our finger can! 243 | 244 | -------------------------------------------------------------------------------- /src/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.md) 15 | 2. [The Stack Allocated List](infinity-stack-allocated.md) 16 | 3. The Self-Referential Arena List? 17 | 4. The GhostCell List? -------------------------------------------------------------------------------- /src/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 | pub struct List { 7 | head: Link, 8 | } 9 | 10 | type Link = Option>>; 11 | 12 | struct Node { 13 | elem: T, 14 | next: Link, 15 | } 16 | 17 | impl List { 18 | pub fn new() -> Self { 19 | List { head: None } 20 | } 21 | 22 | pub fn push(&mut self, elem: T) { 23 | let new_node = Box::new(Node { 24 | elem: elem, 25 | next: self.head.take(), 26 | }); 27 | 28 | self.head = Some(new_node); 29 | } 30 | 31 | pub fn pop(&mut self) -> Option { 32 | self.head.take().map(|node| { 33 | self.head = node.next; 34 | node.elem 35 | }) 36 | } 37 | 38 | pub fn peek(&self) -> Option<&T> { 39 | self.head.as_ref().map(|node| { 40 | &node.elem 41 | }) 42 | } 43 | 44 | pub fn peek_mut(&mut self) -> Option<&mut T> { 45 | self.head.as_mut().map(|node| { 46 | &mut node.elem 47 | }) 48 | } 49 | 50 | pub fn into_iter(self) -> IntoIter { 51 | IntoIter(self) 52 | } 53 | 54 | pub fn iter(&self) -> Iter<'_, T> { 55 | Iter { next: self.head.as_deref() } 56 | } 57 | 58 | pub fn iter_mut(&mut self) -> IterMut<'_, T> { 59 | IterMut { next: self.head.as_deref_mut() } 60 | } 61 | } 62 | 63 | impl Drop for List { 64 | fn drop(&mut self) { 65 | let mut cur_link = self.head.take(); 66 | while let Some(mut boxed_node) = cur_link { 67 | cur_link = boxed_node.next.take(); 68 | } 69 | } 70 | } 71 | 72 | pub struct IntoIter(List); 73 | 74 | impl Iterator for IntoIter { 75 | type Item = T; 76 | fn next(&mut self) -> Option { 77 | // access fields of a tuple struct numerically 78 | self.0.pop() 79 | } 80 | } 81 | 82 | pub struct Iter<'a, T> { 83 | next: Option<&'a Node>, 84 | } 85 | 86 | impl<'a, T> Iterator for Iter<'a, T> { 87 | type Item = &'a T; 88 | fn next(&mut self) -> Option { 89 | self.next.map(|node| { 90 | self.next = node.next.as_deref(); 91 | &node.elem 92 | }) 93 | } 94 | } 95 | 96 | pub struct IterMut<'a, T> { 97 | next: Option<&'a mut Node>, 98 | } 99 | 100 | impl<'a, T> Iterator for IterMut<'a, T> { 101 | type Item = &'a mut T; 102 | 103 | fn next(&mut self) -> Option { 104 | self.next.take().map(|node| { 105 | self.next = node.next.as_deref_mut(); 106 | &mut node.elem 107 | }) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod test { 113 | use super::List; 114 | 115 | #[test] 116 | fn basics() { 117 | let mut list = List::new(); 118 | 119 | // Check empty list behaves right 120 | assert_eq!(list.pop(), None); 121 | 122 | // Populate list 123 | list.push(1); 124 | list.push(2); 125 | list.push(3); 126 | 127 | // Check normal removal 128 | assert_eq!(list.pop(), Some(3)); 129 | assert_eq!(list.pop(), Some(2)); 130 | 131 | // Push some more just to make sure nothing's corrupted 132 | list.push(4); 133 | list.push(5); 134 | 135 | // Check normal removal 136 | assert_eq!(list.pop(), Some(5)); 137 | assert_eq!(list.pop(), Some(4)); 138 | 139 | // Check exhaustion 140 | assert_eq!(list.pop(), Some(1)); 141 | assert_eq!(list.pop(), None); 142 | } 143 | 144 | #[test] 145 | fn peek() { 146 | let mut list = List::new(); 147 | assert_eq!(list.peek(), None); 148 | assert_eq!(list.peek_mut(), None); 149 | list.push(1); list.push(2); list.push(3); 150 | 151 | assert_eq!(list.peek(), Some(&3)); 152 | assert_eq!(list.peek_mut(), Some(&mut 3)); 153 | 154 | list.peek_mut().map(|value| { 155 | *value = 42 156 | }); 157 | 158 | assert_eq!(list.peek(), Some(&42)); 159 | assert_eq!(list.pop(), Some(42)); 160 | } 161 | 162 | #[test] 163 | fn into_iter() { 164 | let mut list = List::new(); 165 | list.push(1); list.push(2); list.push(3); 166 | 167 | let mut iter = list.into_iter(); 168 | assert_eq!(iter.next(), Some(3)); 169 | assert_eq!(iter.next(), Some(2)); 170 | assert_eq!(iter.next(), Some(1)); 171 | assert_eq!(iter.next(), None); 172 | } 173 | 174 | #[test] 175 | fn iter() { 176 | let mut list = List::new(); 177 | list.push(1); list.push(2); list.push(3); 178 | 179 | let mut iter = list.iter(); 180 | assert_eq!(iter.next(), Some(&3)); 181 | assert_eq!(iter.next(), Some(&2)); 182 | assert_eq!(iter.next(), Some(&1)); 183 | } 184 | 185 | #[test] 186 | fn iter_mut() { 187 | let mut list = List::new(); 188 | list.push(1); list.push(2); list.push(3); 189 | 190 | let mut iter = list.iter_mut(); 191 | assert_eq!(iter.next(), Some(&mut 3)); 192 | assert_eq!(iter.next(), Some(&mut 2)); 193 | assert_eq!(iter.next(), Some(&mut 1)); 194 | } 195 | } 196 | 197 | ``` 198 | 199 | Getting beefier! 200 | -------------------------------------------------------------------------------- /src/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 ,ignore 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 | 31 | error[E0107]: wrong number of type arguments: expected 1, found 0 32 | --> src/second.rs:14:6 33 | | 34 | 14 | impl List { 35 | | ^^^^ expected 1 type argument 36 | 37 | error[E0107]: wrong number of type arguments: expected 1, found 0 38 | --> src/second.rs:36:15 39 | | 40 | 36 | impl Drop for List { 41 | | ^^^^ expected 1 type argument 42 | 43 | ``` 44 | 45 | The problem is pretty clear: we're talking about this `List` thing but that's not 46 | real anymore. Like Option and Box, we now always have to talk about 47 | `List`. 48 | 49 | But what's the Something we use in all these impls? Just like List, we want our 50 | implementations to work with *all* the T's. So, just like List, let's make our 51 | `impl`s pointy: 52 | 53 | 54 | ```rust ,ignore 55 | impl List { 56 | pub fn new() -> Self { 57 | List { head: None } 58 | } 59 | 60 | pub fn push(&mut self, elem: T) { 61 | let new_node = Box::new(Node { 62 | elem: elem, 63 | next: self.head.take(), 64 | }); 65 | 66 | self.head = Some(new_node); 67 | } 68 | 69 | pub fn pop(&mut self) -> Option { 70 | self.head.take().map(|node| { 71 | self.head = node.next; 72 | node.elem 73 | }) 74 | } 75 | } 76 | 77 | impl Drop for List { 78 | fn drop(&mut self) { 79 | let mut cur_link = self.head.take(); 80 | while let Some(mut boxed_node) = cur_link { 81 | cur_link = boxed_node.next.take(); 82 | } 83 | } 84 | } 85 | ``` 86 | 87 | ...and that's it! 88 | 89 | 90 | ```text 91 | > cargo test 92 | 93 | Running target/debug/lists-5c71138492ad4b4a 94 | 95 | running 2 tests 96 | test first::test::basics ... ok 97 | test second::test::basics ... ok 98 | 99 | test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured 100 | 101 | ``` 102 | 103 | All of our code is now completely generic over arbitrary values of T. Dang, 104 | Rust is *easy*. I'd like to make a particular shout-out to `new` which didn't 105 | even change: 106 | 107 | ```rust ,ignore 108 | pub fn new() -> Self { 109 | List { head: None } 110 | } 111 | ``` 112 | 113 | Bask in the Glory that is Self, guardian of refactoring and copy-pasta coding. 114 | Also of interest, we don't write `List` when we construct an instance of 115 | list. That part's inferred for us based on the fact that we're returning it 116 | from a function that expects a `List`. 117 | 118 | Alright, let's move on to totally new *behaviour*! 119 | -------------------------------------------------------------------------------- /src/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 ,ignore 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 that it can spit out when you call `next`. 16 | 17 | The reason Iterator yields `Option` is because the interface 18 | coalesces the `has_next` and `get_next` concepts. When you have the next value, 19 | you yield 20 | `Some(value)`, and when you don't you yield `None`. This makes the 21 | API generally more ergonomic and safe to use and implement, while avoiding 22 | redundant checks and logic between `has_next` and `get_next`. Nice! 23 | 24 | Sadly, Rust has nothing like a `yield` statement (yet), so we're going to have to 25 | implement the logic ourselves. Also, there's actually 3 different kinds of 26 | iterator each collection should endeavour to implement: 27 | 28 | * IntoIter - `T` 29 | * IterMut - `&mut T` 30 | * Iter - `&T` 31 | 32 | We actually already have all the tools to implement 33 | IntoIter using List's interface: just call `pop` over and over. As such, we'll 34 | just implement IntoIter as a newtype wrapper around List: 35 | 36 | 37 | ```rust ,ignore 38 | // Tuple structs are an alternative form of struct, 39 | // useful for trivial wrappers around other types. 40 | pub struct IntoIter(List); 41 | 42 | impl List { 43 | pub fn into_iter(self) -> IntoIter { 44 | IntoIter(self) 45 | } 46 | } 47 | 48 | impl Iterator for IntoIter { 49 | type Item = T; 50 | fn next(&mut self) -> Option { 51 | // access fields of a tuple struct numerically 52 | self.0.pop() 53 | } 54 | } 55 | ``` 56 | 57 | And let's write a test: 58 | 59 | ```rust ,ignore 60 | #[test] 61 | fn into_iter() { 62 | let mut list = List::new(); 63 | list.push(1); list.push(2); list.push(3); 64 | 65 | let mut iter = list.into_iter(); 66 | assert_eq!(iter.next(), Some(3)); 67 | assert_eq!(iter.next(), Some(2)); 68 | assert_eq!(iter.next(), Some(1)); 69 | assert_eq!(iter.next(), None); 70 | } 71 | ``` 72 | 73 | ```text 74 | > cargo test 75 | 76 | Running target/debug/lists-5c71138492ad4b4a 77 | 78 | running 4 tests 79 | test first::test::basics ... ok 80 | test second::test::basics ... ok 81 | test second::test::into_iter ... ok 82 | test second::test::peek ... ok 83 | 84 | test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured 85 | 86 | ``` 87 | 88 | Nice! 89 | -------------------------------------------------------------------------------- /src/second-iter-mut.md: -------------------------------------------------------------------------------- 1 | # IterMut 2 | 3 | I'm gonna be honest, IterMut is WILD. Which in itself seems like a wild 4 | thing to say; surely it's identical to Iter! 5 | 6 | Semantically, yes, but 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 ,ignore 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 desugared to: 20 | 21 | ```rust ,ignore 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 ,ignore 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 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 | 54 | We'll start by just taking the Iter code and changing everything to be mutable: 55 | 56 | ```rust ,ignore 57 | pub struct IterMut<'a, T> { 58 | next: Option<&'a mut Node>, 59 | } 60 | 61 | impl List { 62 | pub fn iter_mut(&self) -> IterMut<'_, T> { 63 | IterMut { next: self.head.as_deref_mut() } 64 | } 65 | } 66 | 67 | impl<'a, T> Iterator for IterMut<'a, T> { 68 | type Item = &'a mut T; 69 | 70 | fn next(&mut self) -> Option { 71 | self.next.map(|node| { 72 | self.next = node.next.as_deref_mut(); 73 | &mut node.elem 74 | }) 75 | } 76 | } 77 | ``` 78 | 79 | ```text 80 | > cargo build 81 | error[E0596]: cannot borrow `self.head` as mutable, as it is behind a `&` reference 82 | --> src/second.rs:95:25 83 | | 84 | 94 | pub fn iter_mut(&self) -> IterMut<'_, T> { 85 | | ----- help: consider changing this to be a mutable reference: `&mut self` 86 | 95 | IterMut { next: self.head.as_deref_mut() } 87 | | ^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable 88 | 89 | error[E0507]: cannot move out of borrowed content 90 | --> src/second.rs:103:9 91 | | 92 | 103 | self.next.map(|node| { 93 | | ^^^^^^^^^ cannot move out of borrowed content 94 | ``` 95 | 96 | Ok looks like we've got two different errors here. The first one looks really clear 97 | though, it even tells us how to fix it! You can't upgrade a shared reference to a mutable 98 | one, so `iter_mut` needs to take `&mut self`. Just a silly copy-paste error. 99 | 100 | ```rust ,ignore 101 | pub fn iter_mut(&mut self) -> IterMut<'_, T> { 102 | IterMut { next: self.head.as_deref_mut() } 103 | } 104 | ``` 105 | 106 | What about the other one? 107 | 108 | Oops! I actually accidentally made an error when writing the `iter` impl in 109 | the previous section, and we were just getting lucky that it worked! 110 | 111 | We have just had our first run in with the magic of Copy. When we introduced [ownership][ownership] we 112 | said that when you move stuff, you can't use it anymore. For some types, this 113 | makes perfect sense. Our good friend Box manages an allocation on the heap for 114 | us, and we certainly don't want two pieces of code to think that they need to 115 | free its memory. 116 | 117 | However for other types this is *garbage*. Integers have no 118 | ownership semantics; they're just meaningless numbers! This is why integers are 119 | marked as Copy. Copy types are known to be perfectly copyable by a bitwise copy. 120 | As such, they have a super power: when moved, the old value *is* still usable. 121 | As a consequence, you can even move a Copy type out of a reference without 122 | replacement! 123 | 124 | All numeric primitives in Rust (i32, u64, bool, f32, char, etc...) are Copy. 125 | You can also declare any user-defined type to be Copy as well, as long as 126 | all its components are Copy. 127 | 128 | Critically to why this code was working, shared references are also Copy! 129 | Because `&` is copy, `Option<&>` is *also* Copy. So when we did `self.next.map` it 130 | was fine because the Option was just copied. Now we can't do that, because 131 | `&mut` isn't Copy (if you copied an &mut, you'd have two &mut's to the same 132 | location in memory, which is forbidden). Instead, we should properly `take` 133 | the Option to get it. 134 | 135 | 136 | ```rust ,ignore 137 | fn next(&mut self) -> Option { 138 | self.next.take().map(|node| { 139 | self.next = node.next.as_deref_mut(); 140 | &mut node.elem 141 | }) 142 | } 143 | ``` 144 | 145 | ```text 146 | > cargo build 147 | 148 | ``` 149 | 150 | Uh... wow. Holy shit! IterMut Just Works! 151 | 152 | Let's test this: 153 | 154 | 155 | ```rust ,ignore 156 | #[test] 157 | fn iter_mut() { 158 | let mut list = List::new(); 159 | list.push(1); list.push(2); list.push(3); 160 | 161 | let mut iter = list.iter_mut(); 162 | assert_eq!(iter.next(), Some(&mut 3)); 163 | assert_eq!(iter.next(), Some(&mut 2)); 164 | assert_eq!(iter.next(), Some(&mut 1)); 165 | } 166 | ``` 167 | 168 | ```text 169 | > cargo test 170 | 171 | Running target/debug/lists-5c71138492ad4b4a 172 | 173 | running 6 tests 174 | test first::test::basics ... ok 175 | test second::test::basics ... ok 176 | test second::test::iter_mut ... ok 177 | test second::test::into_iter ... ok 178 | test second::test::iter ... ok 179 | test second::test::peek ... ok 180 | 181 | test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured 182 | 183 | ``` 184 | 185 | Yep. It works. 186 | 187 | Holy shit. 188 | 189 | What. 190 | 191 | Ok I mean it actually *is* supposed to work, but there's usually something 192 | stupid that gets in the way! Let's be clear here: 193 | 194 | We have just implemented a piece of code that takes a singly-linked list, and 195 | returns a mutable reference to every single element in the list at most once. 196 | And it's statically verified to do that. And it's totally safe. And we didn't 197 | have to do anything wild. 198 | 199 | That's kind of a big deal, if you ask me. There are a couple reasons why 200 | this works: 201 | 202 | * We `take` the `Option<&mut>` so we have exclusive access to the mutable 203 | reference. No need to worry about someone looking at it again. 204 | * Rust understands that it's ok to shard a mutable reference into the subfields 205 | of the pointed-to struct, because there's no way to "go back up", and they're 206 | definitely disjoint. 207 | 208 | It turns out that you can apply this basic logic to get a safe IterMut for an 209 | array or a tree as well! You can even make the iterator DoubleEnded, so that 210 | you can consume the iterator from the front *and* the back at once! Woah! 211 | 212 | [ownership]: first-ownership.md 213 | -------------------------------------------------------------------------------- /src/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 ,ignore 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 ,ignore 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 | self.head = node.next; 54 | Some(node.elem) 55 | } 56 | } 57 | } 58 | } 59 | 60 | impl Drop for List { 61 | fn drop(&mut self) { 62 | let mut cur_link = mem::replace(&mut self.head, None); 63 | while let Some(mut boxed_node) = cur_link { 64 | cur_link = mem::replace(&mut boxed_node.next, None); 65 | } 66 | } 67 | } 68 | ``` 69 | 70 | This is marginally better, but the big wins will come from Option's methods. 71 | 72 | First, `mem::replace(&mut option, None)` is such an incredibly 73 | common idiom that Option actually just went ahead and made it a method: `take`. 74 | 75 | ```rust ,ignore 76 | pub struct List { 77 | head: Link, 78 | } 79 | 80 | type Link = Option>; 81 | 82 | struct Node { 83 | elem: i32, 84 | next: Link, 85 | } 86 | 87 | impl List { 88 | pub fn new() -> Self { 89 | List { head: None } 90 | } 91 | 92 | pub fn push(&mut self, elem: i32) { 93 | let new_node = Box::new(Node { 94 | elem: elem, 95 | next: self.head.take(), 96 | }); 97 | 98 | self.head = Some(new_node); 99 | } 100 | 101 | pub fn pop(&mut self) -> Option { 102 | match self.head.take() { 103 | None => None, 104 | Some(node) => { 105 | self.head = node.next; 106 | Some(node.elem) 107 | } 108 | } 109 | } 110 | } 111 | 112 | impl Drop for List { 113 | fn drop(&mut self) { 114 | let mut cur_link = self.head.take(); 115 | while let Some(mut boxed_node) = cur_link { 116 | cur_link = boxed_node.next.take(); 117 | } 118 | } 119 | } 120 | ``` 121 | 122 | Second, `match option { None => None, Some(x) => Some(y) }` is such an 123 | incredibly common idiom that it was called `map`. `map` takes a function to 124 | execute on the `x` in the `Some(x)` to produce the `y` in `Some(y)`. We could 125 | write a proper `fn` and pass it to `map`, but we'd much rather write what to 126 | do *inline*. 127 | 128 | The way to do this is with a *closure*. Closures are anonymous functions with 129 | an extra super-power: they can refer to local variables *outside* the closure! 130 | This makes them super useful for doing all sorts of conditional logic. The 131 | only place we do a `match` is in `pop`, so let's just rewrite that: 132 | 133 | ```rust ,ignore 134 | pub fn pop(&mut self) -> Option { 135 | self.head.take().map(|node| { 136 | self.head = node.next; 137 | node.elem 138 | }) 139 | } 140 | ``` 141 | 142 | Ah, much better. Let's make sure we didn't break anything: 143 | 144 | ```text 145 | > cargo test 146 | 147 | Running target/debug/lists-5c71138492ad4b4a 148 | 149 | running 2 tests 150 | test first::test::basics ... ok 151 | test second::test::basics ... ok 152 | 153 | test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured 154 | 155 | ``` 156 | 157 | Great! Let's move on to actually improving the code's *behaviour*. 158 | -------------------------------------------------------------------------------- /src/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 | ```rust ,ignore 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 | 19 | error[E0515]: cannot return reference to local data `node.elem` 20 | --> src/second.rs:37:13 21 | | 22 | 37 | &node.elem 23 | | ^^^^^^^^^^ returns a reference to data owned by the current function 24 | 25 | error[E0507]: cannot move out of borrowed content 26 | --> src/second.rs:36:9 27 | | 28 | 36 | self.head.map(|node| { 29 | | ^^^^^^^^^ cannot move out of borrowed content 30 | 31 | 32 | ``` 33 | 34 | *Sigh*. What now, Rust? 35 | 36 | Map takes `self` by value, which would move the Option out of the thing it's in. 37 | Previously this was fine because we had just `take`n it out, but now we actually 38 | want to leave it where it was. The *correct* way to handle this is with the 39 | `as_ref` method on Option, which has the following definition: 40 | 41 | ```rust ,ignore 42 | impl Option { 43 | pub fn as_ref(&self) -> Option<&T>; 44 | } 45 | ``` 46 | 47 | It demotes the `Option` to an Option to a reference to its internals. We could 48 | do this ourselves with an explicit match but *ugh no*. It does mean that we 49 | need to do an extra dereference to cut through the extra indirection, but 50 | thankfully the `.` operator handles that for us. 51 | 52 | 53 | ```rust ,ignore 54 | pub fn peek(&self) -> Option<&T> { 55 | self.head.as_ref().map(|node| { 56 | &node.elem 57 | }) 58 | } 59 | ``` 60 | 61 | ```text 62 | cargo build 63 | 64 | Finished dev [unoptimized + debuginfo] target(s) in 0.32s 65 | ``` 66 | 67 | Nailed it. 68 | 69 | We can also make a *mutable* version of this method using `as_mut`: 70 | 71 | ```rust ,ignore 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 | 79 | ```text 80 | > cargo build 81 | 82 | ``` 83 | 84 | EZ 85 | 86 | Don't forget to test it: 87 | 88 | ```rust ,ignore 89 | #[test] 90 | fn peek() { 91 | let mut list = List::new(); 92 | assert_eq!(list.peek(), None); 93 | assert_eq!(list.peek_mut(), None); 94 | list.push(1); list.push(2); list.push(3); 95 | 96 | assert_eq!(list.peek(), Some(&3)); 97 | assert_eq!(list.peek_mut(), Some(&mut 3)); 98 | } 99 | ``` 100 | 101 | ```text 102 | cargo test 103 | 104 | Running target/debug/lists-5c71138492ad4b4a 105 | 106 | running 3 tests 107 | test first::test::basics ... ok 108 | test second::test::basics ... ok 109 | test second::test::peek ... ok 110 | 111 | test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured 112 | 113 | ``` 114 | 115 | That's nice, but we didn't really test to see if we could mutate that `peek_mut` return value, did we? If a reference is mutable but nobody mutates it, have we really tested the mutability? Let's try using `map` on this `Option<&mut T>` to put a profound value in: 116 | 117 | ```rust ,ignore 118 | #[test] 119 | fn peek() { 120 | let mut list = List::new(); 121 | assert_eq!(list.peek(), None); 122 | assert_eq!(list.peek_mut(), None); 123 | list.push(1); list.push(2); list.push(3); 124 | 125 | assert_eq!(list.peek(), Some(&3)); 126 | assert_eq!(list.peek_mut(), Some(&mut 3)); 127 | list.peek_mut().map(|&mut value| { 128 | value = 42 129 | }); 130 | 131 | assert_eq!(list.peek(), Some(&42)); 132 | assert_eq!(list.pop(), Some(42)); 133 | } 134 | ``` 135 | 136 | ```text 137 | > cargo test 138 | 139 | error[E0384]: cannot assign twice to immutable variable `value` 140 | --> src/second.rs:100:13 141 | | 142 | 99 | list.peek_mut().map(|&mut value| { 143 | | ----- 144 | | | 145 | | first assignment to `value` 146 | | help: make this binding mutable: `mut value` 147 | 100 | value = 42 148 | | ^^^^^^^^^^ cannot assign twice to immutable variable ^~~~~ 149 | ``` 150 | 151 | The compiler is complaining that `value` is immutable, but we pretty clearly wrote `&mut value`; what gives? It turns out that writing the argument of the closure that way doesn't specify that `value` is a mutable reference. Instead, it creates a pattern that will be matched against the argument to the closure; `|&mut value|` means "the argument is a mutable reference, but just copy the value it points to into `value`, please." If we just use `|value|`, the type of `value` will be `&mut i32` and we can actually mutate the head: 152 | 153 | ```rust ,ignore 154 | #[test] 155 | fn peek() { 156 | let mut list = List::new(); 157 | assert_eq!(list.peek(), None); 158 | assert_eq!(list.peek_mut(), None); 159 | list.push(1); list.push(2); list.push(3); 160 | 161 | assert_eq!(list.peek(), Some(&3)); 162 | assert_eq!(list.peek_mut(), Some(&mut 3)); 163 | 164 | list.peek_mut().map(|value| { 165 | *value = 42 166 | }); 167 | 168 | assert_eq!(list.peek(), Some(&42)); 169 | assert_eq!(list.pop(), Some(42)); 170 | } 171 | ``` 172 | 173 | ```text 174 | cargo test 175 | 176 | Running target/debug/lists-5c71138492ad4b4a 177 | 178 | running 3 tests 179 | test first::test::basics ... ok 180 | test second::test::basics ... ok 181 | test second::test::peek ... ok 182 | 183 | test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured 184 | 185 | ``` 186 | 187 | Much better! 188 | -------------------------------------------------------------------------------- /src/second.md: -------------------------------------------------------------------------------- 1 | # An Ok Singly-Linked Stack 2 | 3 | In the previous chapter we wrote up 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 | ```rust ,ignore 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 | -------------------------------------------------------------------------------- /src/sixth-basics.md: -------------------------------------------------------------------------------- 1 | # Basics 2 | 3 | Alright, this is the part of the book that sucks, and why it took me 7 years to write this chapter! Time to just burn through a whole lot of really boring stuff we've done 5 times already, but extra verbose and long because we have to do everything twice and with `Option>>`! 4 | 5 | ```rust ,ignore 6 | impl LinkedList { 7 | pub fn new() -> Self { 8 | Self { 9 | front: None, 10 | back: None, 11 | len: 0, 12 | _boo: PhantomData, 13 | } 14 | } 15 | } 16 | ``` 17 | 18 | PhantomData is a weird type with no fields so you just make one by, saying its type name. *shrug* 19 | 20 | ```rust ,ignore 21 | pub fn push_front(&mut self, elem: T) { 22 | // SAFETY: it's a linked-list, what do you want? 23 | unsafe { 24 | let new = NonNull::new_unchecked(Box::into_raw(Box::new(Node { 25 | front: None, 26 | back: None, 27 | elem, 28 | }))); 29 | if let Some(old) = self.front { 30 | // Put the new front before the old one 31 | (*old).front = Some(new); 32 | (*new).back = Some(old); 33 | } else { 34 | // If there's no front, then we're the empty list and need 35 | // to set the back too. Also here's some integrity checks 36 | // for testing, in case we mess up. 37 | debug_assert!(self.back.is_none()); 38 | debug_assert!(self.front.is_none()); 39 | debug_assert!(self.len == 0); 40 | self.back = Some(new); 41 | } 42 | self.front = Some(new); 43 | self.len += 1; 44 | } 45 | } 46 | ``` 47 | 48 | ```text 49 | error[E0614]: type `NonNull>` cannot be dereferenced 50 | --> src\lib.rs:39:17 51 | | 52 | 39 | (*old).front = Some(new); 53 | | ^^^^^^ 54 | ``` 55 | 56 | 57 | Ah yes, I truly hate my pointer-y children. We need to explicitly get the raw pointer out of NonNull with `as_ptr`, because DerefMut is defined in terms of `&mut` and we don't want to randomly introduce safe references into our unsafe code! 58 | 59 | 60 | ```rust ,ignore 61 | (*old.as_ptr()).front = Some(new); 62 | (*new.as_ptr()).back = Some(old); 63 | ``` 64 | 65 | ```text 66 | Compiling linked-list v0.0.3 67 | warning: field is never read: `elem` 68 | --> src\lib.rs:16:5 69 | | 70 | 16 | elem: T, 71 | | ^^^^^^^ 72 | | 73 | = note: `#[warn(dead_code)]` on by default 74 | 75 | warning: `linked-list` (lib) generated 1 warning (1 duplicate) 76 | warning: `linked-list` (lib test) generated 1 warning 77 | Finished test [unoptimized + debuginfo] target(s) in 0.33s 78 | ``` 79 | 80 | Nice, now for pop (and len): 81 | 82 | ```rust ,ignore 83 | pub fn pop_front(&mut self) -> Option { 84 | unsafe { 85 | // Only have to do stuff if there is a front node to pop. 86 | // Note that we don't need to mess around with `take` anymore 87 | // because everything is Copy and there are no dtors that will 88 | // run if we mess up... right? :) Riiiight? :))) 89 | self.front.map(|node| { 90 | // Bring the Box back to life so we can move out its value and 91 | // Drop it (Box continues to magically understand this for us). 92 | let boxed_node = Box::from_raw(node.as_ptr()); 93 | let result = boxed_node.elem; 94 | 95 | // Make the next node into the new front. 96 | self.front = boxed_node.back; 97 | if let Some(new) = self.front { 98 | // Cleanup its reference to the removed node 99 | (*new.as_ptr()).front = None; 100 | } else { 101 | // If the front is now null, then this list is now empty! 102 | debug_assert!(self.len == 1); 103 | self.back = None; 104 | } 105 | 106 | self.len -= 1; 107 | result 108 | // Box gets implicitly freed here, knows there is no T. 109 | }) 110 | } 111 | } 112 | 113 | pub fn len(&self) -> usize { 114 | self.len 115 | } 116 | ``` 117 | 118 | ```text 119 | Compiling linked-list v0.0.3 120 | Finished dev [unoptimized + debuginfo] target(s) in 0.37s 121 | ``` 122 | 123 | Seems legit to me, time to write a test! 124 | 125 | ```rust ,ignore 126 | #[cfg(test)] 127 | mod test { 128 | use super::LinkedList; 129 | 130 | #[test] 131 | fn test_basic_front() { 132 | let mut list = LinkedList::new(); 133 | 134 | // Try to break an empty list 135 | assert_eq!(list.len(), 0); 136 | assert_eq!(list.pop_front(), None); 137 | assert_eq!(list.len(), 0); 138 | 139 | // Try to break a one item list 140 | list.push_front(10); 141 | assert_eq!(list.len(), 1); 142 | assert_eq!(list.pop_front(), Some(10)); 143 | assert_eq!(list.len(), 0); 144 | assert_eq!(list.pop_front(), None); 145 | assert_eq!(list.len(), 0); 146 | 147 | // Mess around 148 | list.push_front(10); 149 | assert_eq!(list.len(), 1); 150 | list.push_front(20); 151 | assert_eq!(list.len(), 2); 152 | list.push_front(30); 153 | assert_eq!(list.len(), 3); 154 | assert_eq!(list.pop_front(), Some(30)); 155 | assert_eq!(list.len(), 2); 156 | list.push_front(40); 157 | assert_eq!(list.len(), 3); 158 | assert_eq!(list.pop_front(), Some(40)); 159 | assert_eq!(list.len(), 2); 160 | assert_eq!(list.pop_front(), Some(20)); 161 | assert_eq!(list.len(), 1); 162 | assert_eq!(list.pop_front(), Some(10)); 163 | assert_eq!(list.len(), 0); 164 | assert_eq!(list.pop_front(), None); 165 | assert_eq!(list.len(), 0); 166 | assert_eq!(list.pop_front(), None); 167 | assert_eq!(list.len(), 0); 168 | } 169 | } 170 | ``` 171 | 172 | 173 | ```text 174 | Compiling linked-list v0.0.3 175 | Finished test [unoptimized + debuginfo] target(s) in 0.40s 176 | Running unittests src\lib.rs 177 | 178 | running 1 test 179 | test test::test_basic_front ... ok 180 | 181 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s 182 | ``` 183 | 184 | Hooray, we're perfect! 185 | 186 | ...Right? -------------------------------------------------------------------------------- /src/sixth-layout.md: -------------------------------------------------------------------------------- 1 | # Layout 2 | 3 | Let us begin by first studying the structure of our enemy. A Doubly-Linked List is conceptually simple, but that's how it decieves and manipulates you. It's the same kind of linked list we've looked at over and over, but the links go both ways. Double the links, double the evil. 4 | 5 | So rather than this (gonna drop the Some/None stuff to keep it cleaner): 6 | 7 | ```text 8 | ... -> (A, ptr) -> (B, ptr) -> ... 9 | ``` 10 | 11 | We have this: 12 | 13 | ```text 14 | ... <-> (ptr, A, ptr) <-> (ptr, B, ptr) <-> ... 15 | ``` 16 | 17 | This lets you traverse the list from either direction, or seek back and forth with a [cursor](https://doc.rust-lang.org/std/collections/struct.LinkedList.html#method.cursor_back_mut). 18 | 19 | In exchange for this flexibility, every node has to store twice as many pointers, and every operation has to fix up way more pointers. It's a significant enough complication that it's a lot easier to make a mistake, so we're going to be doing a lot of testing. 20 | 21 | You might have also noticed that I intentionally haven't drawn the *ends* of the list. This is because this is one of the places where there are genuinely defensible options for our implementation. We *definitely* need our implementation to have two pointers: one to the start of the list, and one to the end of the list. 22 | 23 | There are two notable ways to do this in my mind: "traditional" and "dummy node". 24 | 25 | The traditional approach is the simple extension of how we did a Stack — just store the head and tail pointers on the stack: 26 | 27 | ```text 28 | [ptr, ptr] <-> (ptr, A, ptr) <-> (ptr, B, ptr) 29 | ^ ^ 30 | +----------------------------------------+ 31 | ``` 32 | 33 | This is fine, but it has one downside: corner cases. There are now two edges to our list, which means twice as many corner cases. It's easy to forget one and have a serious bug. 34 | 35 | The dummy node approach attempts to smooth out these corner cases by adding an extra node to our list which contains no data but links the two ends together into a ring: 36 | 37 | ```text 38 | [ptr] -> (ptr, ?DUMMY?, ptr) <-> (ptr, A, ptr) <-> (ptr, B, ptr) 39 | ^ ^ 40 | +-------------------------------------------------+ 41 | ``` 42 | 43 | By doing this, every node *always* has actual pointers to a previous and next node in the list. Even when you remove the last element from the list, you just end up stitching the dummy node to point at itself: 44 | 45 | ```text 46 | [ptr] -> (ptr, ?DUMMY?, ptr) 47 | ^ ^ 48 | +-------------+ 49 | ``` 50 | 51 | There is a part of me that finds this *very* satisfying and elegant. Unfortunately, it has a couple practical problems: 52 | 53 | Problem 1: An extra indirection and allocation, especially for the empty list, which must include the dummy node. Potential solutions include: 54 | 55 | * Don't allocate the dummy node until something is inserted: simple and effective, but it adds back some of the corner cases we were trying to avoid by using dummy pointers! 56 | 57 | * Use a static copy-on-write empty singleton dummy node, with some really clever scheme that lets the Copy-On-Write checks piggy-back on normal checks: look I'm really tempted, I really do love that shit, but we can't go down that dark path in this book. Read [ThinVec's sourcecode](https://docs.rs/thin-vec/0.2.4/src/thin_vec/lib.rs.html#319-325) if you want to see that kind of perverted stuff. 58 | 59 | * Store the dummy node on the stack - not practical in a language without C++-style move-constructors. I'm sure there's something weird thing we could do here with [pinning](https://doc.rust-lang.org/std/pin/index.html) but we're not gonna. 60 | 61 | Problem 2: What *value* is stored in the dummy node? Sure if it's an integer it's fine, but what if we're storing a list full of `Box`? It may be impossible for us to initialized this value! Potential solutions include: 62 | 63 | * Make every node store `Option`: simple and effective, but also bloated and annoying. 64 | 65 | * Make every node store [`MaybeUninit`](https://doc.rust-lang.org/std/mem/union.MaybeUninit.html). Horrifying and annoying. 66 | 67 | * *Really* careful and clever inheritance-style type punning so the dummy node doesn't include the data field. This is also tempting but it's extremely dangerous and annoying. Read [BTreeMap's source](https://doc.rust-lang.org/1.55.0/src/alloc/collections/btree/node.rs.html#49-104) if you want to see that kind of perverted stuff. 68 | 69 | The problems really outweigh the convenience for a language like Rust, so we're going to stick to the traditional layout. We'll be using the same basic design as we did for the unsafe queue in the previous chapter: 70 | 71 | ```rust 72 | pub struct LinkedList { 73 | front: Link, 74 | back: Link, 75 | len: usize, 76 | } 77 | 78 | type Link = *mut Node; 79 | 80 | struct Node { 81 | front: Link, 82 | back: Link, 83 | elem: T, 84 | } 85 | ``` 86 | 87 | (Now that we have reached the doubly-linked-deque, we have finally earned the right to call ourselves LinkedList, for this is the True Linked List.) 88 | 89 | This isn't quite a *true* production-quality layout yet. It's *fine* but there's magic tricks we can do to tell Rust what we're doing a bit better. To do that we're going to need to go... deeper. -------------------------------------------------------------------------------- /src/sixth-send-sync.md: -------------------------------------------------------------------------------- 1 | # Send, Sync, and Compile Tests 2 | 3 | Ok actually we do have one more pair of traits to think about, but they're special. We have to deal with Rust's Holy Roman Empire: The Unsafe Opt-In Built-In Traits (OIBITs): [Send and Sync](https://doc.rust-lang.org/nomicon/send-and-sync.html), which are in fact opt-out and built-out (1 out of 3 is pretty good!). 4 | 5 | Like Copy, these traits have absolutely no code associated with them, and are just markers that your type has a particular property. Send says that your type is safe to send to another thread. Sync says your type is safe to share between threads (&Self: Send). 6 | 7 | The same argument for LinkedList being covariant applies here: generally normal collections which don't use fancy interior mutability tricks are safe to make Send and Sync. 8 | 9 | But I said they're *opt out*. So actually, are we already? How would we know? 10 | 11 | Let's add some new magic to our code: random private garbage that won't compile unless our types have the properties we expect: 12 | 13 | ```rust ,ignore 14 | #[allow(dead_code)] 15 | fn assert_properties() { 16 | fn is_send() {} 17 | fn is_sync() {} 18 | 19 | is_send::>(); 20 | is_sync::>(); 21 | 22 | is_send::>(); 23 | is_sync::>(); 24 | 25 | is_send::>(); 26 | is_sync::>(); 27 | 28 | is_send::>(); 29 | is_sync::>(); 30 | 31 | is_send::>(); 32 | is_sync::>(); 33 | 34 | fn linked_list_covariant<'a, T>(x: LinkedList<&'static T>) -> LinkedList<&'a T> { x } 35 | fn iter_covariant<'i, 'a, T>(x: Iter<'i, &'static T>) -> Iter<'i, &'a T> { x } 36 | fn into_iter_covariant<'a, T>(x: IntoIter<&'static T>) -> IntoIter<&'a T> { x } 37 | } 38 | ``` 39 | 40 | ```text 41 | cargo build 42 | Compiling linked-list v0.0.3 43 | error[E0277]: `NonNull>` cannot be sent between threads safely 44 | --> src\lib.rs:433:5 45 | | 46 | 433 | is_send::>(); 47 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `NonNull>` cannot be sent between threads safely 48 | | 49 | = help: within `LinkedList`, the trait `Send` is not implemented for `NonNull>` 50 | = note: required because it appears within the type `Option>>` 51 | note: required because it appears within the type `LinkedList` 52 | --> src\lib.rs:8:12 53 | | 54 | 8 | pub struct LinkedList { 55 | | ^^^^^^^^^^ 56 | note: required by a bound in `is_send` 57 | --> src\lib.rs:430:19 58 | | 59 | 430 | fn is_send() {} 60 | | ^^^^ required by this bound in `is_send` 61 | 62 | 63 | ``` 64 | 65 | Oh geez, what gives! I had that great Holy Roman Empire joke! 66 | 67 | Well, I lied to you when I said raw pointers have only one safety guard: this is the other. `*const` AND `*mut` explicitly opt out of Send and Sync to be safe, so we do *actually* have to opt back in: 68 | 69 | ```rust ,ignore 70 | unsafe impl Send for LinkedList {} 71 | unsafe impl Sync for LinkedList {} 72 | 73 | unsafe impl<'a, T: Send> Send for Iter<'a, T> {} 74 | unsafe impl<'a, T: Sync> Sync for Iter<'a, T> {} 75 | 76 | unsafe impl<'a, T: Send> Send for IterMut<'a, T> {} 77 | unsafe impl<'a, T: Sync> Sync for IterMut<'a, T> {} 78 | ``` 79 | 80 | Note that we have to write *unsafe impl* here: these are *unsafe traits*! Unsafe code (like concurrency libraries) gets to rely on us only implementing these traits correctly! Since there's no actual code, the guarantee we're making is just that, yes, we are indeed safe to Send or Share between threads! 81 | 82 | Don't just slap these on lightly, but I am a Certified Professional here to say: yep there's are totally fine. Note how we don't need to implement Send and Sync for IntoIter: it just contains LinkedList, so it auto-derives Send and Sync — I told you they were actually opt out! (You opt out with the hillarious syntax of `impl !Send for MyType {}`.) 83 | 84 | ```text 85 | cargo build 86 | Compiling linked-list v0.0.3 87 | Finished dev [unoptimized + debuginfo] target(s) in 0.18s 88 | ``` 89 | 90 | Ok nice! 91 | 92 | ...Wait, actually it would be really dangerous if stuff that *shouldn't* be these things wasn't. In particular, IterMut *definitely* shouldn't be covariant, because it's "like" `&mut T`. But how can we check that? 93 | 94 | With Magic! Well, actually, with rustdoc! Ok well we don't have to use rustdoc for this, but it's the funniest way to do it. See, if you write a doccomment and include a code block, then rustdoc will try to compile and run it, so we can use that to make fresh anonymous "programs" that don't affect the main one: 95 | 96 | 97 | ```rust ,ignore 98 | /// ``` 99 | /// use linked_list::IterMut; 100 | /// 101 | /// fn iter_mut_covariant<'i, 'a, T>(x: IterMut<'i, &'static T>) -> IterMut<'i, &'a T> { x } 102 | /// ``` 103 | fn iter_mut_invariant() {} 104 | ``` 105 | 106 | ```text 107 | cargo test 108 | 109 | ... 110 | 111 | Doc-tests linked-list 112 | 113 | running 1 test 114 | test src\lib.rs - assert_properties::iter_mut_invariant (line 458) ... FAILED 115 | 116 | failures: 117 | 118 | ---- src\lib.rs - assert_properties::iter_mut_invariant (line 458) stdout ---- 119 | error[E0308]: mismatched types 120 | --> src\lib.rs:461:86 121 | | 122 | 6 | fn iter_mut_covariant<'i, 'a, T>(x: IterMut<'i, &'static T>) -> IterMut<'i, &'a T> { x } 123 | | ^ lifetime mismatch 124 | | 125 | = note: expected struct `linked_list::IterMut<'_, &'a T>` 126 | found struct `linked_list::IterMut<'_, &'static T>` 127 | ``` 128 | 129 | Ok cool, we've proved it's invariant, but uh, now our tests fail. No worries, rustdoc lets you say that's expected by annotating the fence with compile_fail! 130 | 131 | (Actually we only proved it's "not covariant" but honestly if you manage to make a type "accidentaly and incorrectly contravariant" then, congrats?) 132 | 133 | ```rust ,ignore 134 | /// ```compile_fail 135 | /// use linked_list::IterMut; 136 | /// 137 | /// fn iter_mut_covariant<'i, 'a, T>(x: IterMut<'i, &'static T>) -> IterMut<'i, &'a T> { x } 138 | /// ``` 139 | fn iter_mut_invariant() {} 140 | ``` 141 | 142 | ```text 143 | cargo test 144 | Compiling linked-list v0.0.3 145 | Finished test [unoptimized + debuginfo] target(s) in 0.49s 146 | Running unittests src\lib.rs 147 | 148 | ... 149 | 150 | Doc-tests linked-list 151 | 152 | running 1 test 153 | test src\lib.rs - assert_properties::iter_mut_invariant (line 458) - compile fail ... ok 154 | 155 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s 156 | ``` 157 | 158 | Yay! I recommend always making the test without compile_fail so that you can confirm that it fails to compile *for the right reason*. For instance, that test will also fail (and therefore pass) if you forget the `use`, which, is not what we want! While it's conceptually appealing to be able to "require" a specific error from the compiler, this would be an absolute nightmare that would effectively make it a breaking change *for the compiler to produce better errors*. We want the compiler to get better, so, no you don't get to have that. 159 | 160 | (Oh wait, we can actually just specify the error code we want next to the compile_fail **but this only works on nightly and is a bad idea to rely on for the reasons state above. It will be silently ignored on not-nightly.**) 161 | 162 | ```rust ,ignore 163 | /// ```compile_fail,E0308 164 | /// use linked_list::IterMut; 165 | /// 166 | /// fn iter_mut_covariant<'i, 'a, T>(x: IterMut<'i, &'static T>) -> IterMut<'i, &'a T> { x } 167 | /// ``` 168 | fn iter_mut_invariant() {} 169 | ``` 170 | 171 | ...also, did you notice the part where we actually made IterMut invariant? It was easy to miss, since I "just" copy-pasted Iter and dumped it at the end. It's the last line here: 172 | 173 | ```rust ,ignore 174 | pub struct IterMut<'a, T> { 175 | front: Link, 176 | back: Link, 177 | len: usize, 178 | _boo: PhantomData<&'a mut T>, 179 | } 180 | ``` 181 | 182 | Let's try removing that PhantomData: 183 | 184 | ```text 185 | cargo build 186 | Compiling linked-list v0.0.3 (C:\Users\ninte\dev\contain\linked-list) 187 | error[E0392]: parameter `'a` is never used 188 | --> src\lib.rs:30:20 189 | | 190 | 30 | pub struct IterMut<'a, T> { 191 | | ^^ unused parameter 192 | | 193 | = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` 194 | ``` 195 | 196 | Ha! The compiler has our back and won't just let us *not* use the lifetime. Let's try using the *wrong* example instead: 197 | 198 | ```rust ,ignore 199 | _boo: PhantomData<&'a T>, 200 | ``` 201 | 202 | ```text 203 | cargo build 204 | Compiling linked-list v0.0.3 (C:\Users\ninte\dev\contain\linked-list) 205 | Finished dev [unoptimized + debuginfo] target(s) in 0.17s 206 | ``` 207 | 208 | It builds! Do our tests catch a problem now? 209 | 210 | ```text 211 | cargo test 212 | 213 | ... 214 | 215 | Doc-tests linked-list 216 | 217 | running 1 test 218 | test src\lib.rs - assert_properties::iter_mut_invariant (line 458) - compile fail ... FAILED 219 | 220 | failures: 221 | 222 | ---- src\lib.rs - assert_properties::iter_mut_invariant (line 458) stdout ---- 223 | Test compiled successfully, but it's marked `compile_fail`. 224 | 225 | failures: 226 | src\lib.rs - assert_properties::iter_mut_invariant (line 458) 227 | 228 | test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.15s 229 | ``` 230 | 231 | Eyyy!!! The system works! I love having tests that actually do their job, so that I don't have to be quite so horrified of looming mistakes! 232 | 233 | -------------------------------------------------------------------------------- /src/sixth-variance.md: -------------------------------------------------------------------------------- 1 | # Variance and PhantomData 2 | 3 | It's going to be annoying to punt on this now and fix it later, so we're going to do the Hardcore Layout stuff now. 4 | 5 | There are five terrible horsemen of making unsafe Rust collections: 6 | 7 | 1. [Variance](https://doc.rust-lang.org/nightly/nomicon/subtyping.html) 8 | 2. [Drop Check](https://doc.rust-lang.org/nightly/nomicon/dropck.html) 9 | 3. [NonNull Optimizations](https://doc.rust-lang.org/nightly/std/ptr/struct.NonNull.html) 10 | 4. [The isize::MAX Allocation Rule](https://doc.rust-lang.org/nightly/nomicon/vec/vec-alloc.html) 11 | 5. [Zero-Sized Types](https://doc.rust-lang.org/nightly/nomicon/vec/vec-zsts.html) 12 | 13 | Mercifully, the last 2 aren't going to be a problem for us. 14 | 15 | The third we *could* make into our problem but it's more trouble than it's worth -- if you've opted into a LinkedList you've already given up the battle on memory-effeciency 100-fold already. 16 | 17 | The second is something that I used to insist was really important and that std messes around with, but the defaults are safe, the ways to mess with it are unstable, and you need to try *so very hard* to ever notice the limitations of the defaults, so, don't worry about it. 18 | 19 | That just leaves us with Variance. To be honest, you can probably punt on this one too, but I still have my pride as a Collections Person, so we're going to Do The Variance Thing. 20 | 21 | So, surprise: Rust has subtyping. In particular, `&'big T` is a *subtype* of `&'small T`. Why? Well because if some code needs a reference that lives for some particular region of the program, it's usually perfectly fine to give it a reference that lives for *longer*. Like, intuitively that's just true, right? 22 | 23 | Why is this important? Well imagine some code that takes two values with the same type: 24 | 25 | ```rust ,ignore 26 | fn take_two(_val1: T, _val2: T) { } 27 | ``` 28 | 29 | This is some deeply boring code, and so we should expect it to work with T=&u32 fine, right? 30 | 31 | ```rust 32 | fn two_refs<'big: 'small, 'small>( 33 | big: &'big u32, 34 | small: &'small u32, 35 | ) { 36 | take_two(big, small); 37 | } 38 | 39 | fn take_two(_val1: T, _val2: T) { } 40 | ``` 41 | 42 | Yep, that compiles fine! 43 | 44 | Now let's have some fun and wrap it in, oh, I don't know, `std::cell::Cell`: 45 | 46 | ```rust ,compilefail 47 | use std::cell::Cell; 48 | 49 | fn two_refs<'big: 'small, 'small>( 50 | // NOTE: these two lines changed 51 | big: Cell<&'big u32>, 52 | small: Cell<&'small u32>, 53 | ) { 54 | take_two(big, small); 55 | } 56 | 57 | fn take_two(_val1: T, _val2: T) { } 58 | ``` 59 | 60 | ```text 61 | error[E0623]: lifetime mismatch 62 | --> src/main.rs:7:19 63 | | 64 | 4 | big: Cell<&'big u32>, 65 | | --------- 66 | 5 | small: Cell<&'small u32>, 67 | | ----------- these two types are declared with different lifetimes... 68 | 6 | ) { 69 | 7 | take_two(big, small); 70 | | ^^^^^ ...but data from `small` flows into `big` here 71 | ``` 72 | 73 | Huh??? We didn't touch the lifetimes, why's the compiler angry now!? 74 | 75 | Ah well, the lifetime "subtyping" stuff must be really simple, so it falls over if you wrap the references in anything, see look it breaks with Vec too: 76 | 77 | ```rust 78 | fn two_refs<'big: 'small, 'small>( 79 | big: Vec<&'big u32>, 80 | small: Vec<&'small u32>, 81 | ) { 82 | take_two(big, small); 83 | } 84 | 85 | fn take_two(_val1: T, _val2: T) { } 86 | ``` 87 | 88 | ```text 89 | Finished dev [unoptimized + debuginfo] target(s) in 1.07s 90 | Running `target/debug/playground` 91 | ``` 92 | 93 | See it doesn't compile eith-- wait what??? Vec is magic?????? 94 | 95 | Well, yes. But also, no. The magic was inside us all along, and that magic is ✨*Variance*✨. 96 | 97 | Read the [nomicon's chapter on subtyping](https://doc.rust-lang.org/nightly/nomicon/subtyping.html) if you want all the gorey details, but basically subtyping *isn't* always safe. In particular it's not safe when mutable references are involved because you can use things like `mem::swap` and suddenly oops dangling pointers! 98 | 99 | Things that are "like mutable references" are *invariant* which means they block subtyping from happening on their generic parameters. So for safety, `&mut T` is invariant over T, and `Cell` is invariant over T because `&Cell` is basically just `&mut T` (because of interior mutability). 100 | 101 | Almost everything that isn't invariant is *covariant*, and that just means that subtyping "passes through" it and continues to work normally (there are also contravariant types that make subtyping go backwards but they are really rare and no one likes them so I won't mention them again). 102 | 103 | Collections generally contain a mutable pointer to their data, so you might expect them to be invariant too, but in fact, they don't need to be! Because of Rust's ownership system, `Vec` is semantically equivalent to `T`, and that means it's safe for it to be covariant! 104 | 105 | Unfortunately, this definition is invariant: 106 | 107 | ```rust 108 | pub struct LinkedList { 109 | front: Link, 110 | back: Link, 111 | len: usize, 112 | } 113 | 114 | type Link = *mut Node; 115 | 116 | struct Node { 117 | front: Link, 118 | back: Link, 119 | elem: T, 120 | } 121 | ``` 122 | 123 | But how is Rust actually deciding the variance of things? Well in the good-old-days before 1.0 we messed around with just letting people specify the variance they wanted and... it was an absolute train-wreck! Subtyping and variance is really hard to wrap your head around, and core developers genuinely disagreed on basic terminology! So we moved to a "variance by example" approach: the compiler just looks at your fields and copies their variances. If there's any kind of disagreement, then invariance always wins, because that's safe. 124 | 125 | So what's in our type definitions that Rust is getting mad about? `*mut`! 126 | 127 | Raw pointers in Rust really just try to let you do whatever, but they have exactly one safety feature: because most people have no idea that variance and subtyping are a thing in Rust, and being *incorrectly* covariant would be horribly dangerous, `*mut T` is invariant, because there's a good chance it's being used "as" `&mut T`. 128 | 129 | This is extremely annoying for Exactly Me as a person who has spent a lot of time writing collections in Rust. This is why when I made [std::ptr::NonNull](https://doc.rust-lang.org/std/ptr/struct.NonNull.html), I added this little piece of magic: 130 | 131 | > Unlike `*mut T`, `NonNull` was chosen to be covariant over `T`. This makes it possible to use `NonNull` when building covariant types, but introduces the risk of unsoundness if used in a type that shouldn’t actually be covariant. 132 | 133 | But hey, it's interface is built around `*mut T`, what's the deal! Is it just magic?! Let's look: 134 | 135 | ```rust 136 | pub struct NonNull { 137 | pointer: *const T, 138 | } 139 | 140 | 141 | impl NonNull { 142 | pub unsafe fn new_unchecked(ptr: *mut T) -> Self { 143 | // SAFETY: the caller must guarantee that `ptr` is non-null. 144 | unsafe { NonNull { pointer: ptr as *const T } } 145 | } 146 | } 147 | ``` 148 | 149 | NOPE. NO MAGIC HERE! NonNull just abuses the fact that `*const T` is covariant and stores that instead, casting back and forth between `*mut T` at the API boundary to make it "look like" it's storing a `*mut T`. That's the whole trick! That's how collections in Rust are covariant! And it's miserable! So I made the Good Pointer Type do it for you! You're welcome! Enjoy your subtyping footgun! 150 | 151 | The solution to all your problems is to use NonNull, and then if you want to have nullable pointers again, use `Option>`. Are we really going to bother doing that..? 152 | 153 | Yep! It sucks, but we're making *production grade linked lists* so we're going to eat all our vegetables and do things the hard way (we could just use bare `*const T` and cast everywhere, but I genuinely want to see how painful this is... for Ergonomics Science). 154 | 155 | 156 | So here's our final type definitions: 157 | 158 | ```rust 159 | use std::ptr::NonNull; 160 | 161 | // !!!This changed!!! 162 | pub struct LinkedList { 163 | front: Link, 164 | back: Link, 165 | len: usize, 166 | } 167 | 168 | type Link = Option>>; 169 | 170 | struct Node { 171 | front: Link, 172 | back: Link, 173 | elem: T, 174 | } 175 | ``` 176 | 177 | ...wait nope, one last thing. Any time you do raw pointer stuff, you should add a Ghost to protect your pointers: 178 | 179 | ```rust ,ignore 180 | use std::marker::PhantomData; 181 | 182 | pub struct LinkedList { 183 | front: Link, 184 | back: Link, 185 | len: usize, 186 | /// We semantically store values of T by-value. 187 | _boo: PhantomData, 188 | } 189 | ``` 190 | 191 | In this case I don't think we *actually* need [PhantomData](https://doc.rust-lang.org/std/marker/struct.PhantomData.html), but any time you *do* use NonNull (or just raw pointers in general), you should always add it to be safe and make it clear to the compiler and others what you *think* you're doing. 192 | 193 | PhantomData is a way for us to give the compiler an extra "example" field that *conceptually* exists in your type but for various reasons (indirection, type erasure, ...) doesn't. In this case we're using NonNull because we're claiming our type behaves "as if" it stored a value T, so we add a PhantomData to make that explicit. 194 | 195 | The stdlib actually has other reasons to do this because it has access to the accursed [Drop Check overrides](https://doc.rust-lang.org/nightly/nomicon/dropck.html), but that feature has been reworked so many times that I don't actually know if the PhantomData thing *is* a thing for it anymore. I'm still going to cargo-cult it for all eternity, because Drop Check Magic is burned into my brain! 196 | 197 | (Node literally stores a T, so it doesn't have to do this, yay!) 198 | 199 | ...ok for real we're done with layout now! On to actual basic functionality! 200 | -------------------------------------------------------------------------------- /src/sixth.md: -------------------------------------------------------------------------------- 1 | # A Production-Quality Unsafe Doubly-Linked Deque 2 | 3 | We finally made it. My greatests nemesis: **[std::collections::LinkedList][linked-list], the Doubly-Linked Deque**. 4 | 5 | The one that I tried and failed to destroy. 6 | 7 | Our story begins as 2014 was coming to a close and we were rapidly approaching the release of Rust 1.0, Rust's first stable release. I had found myself in the role of caring for `std::collections`, or as we affectionately called it in those times, libcollections. 8 | 9 | libcollections had spent years as a dumping ground for everyone's Cute Ideas and Vaguely Useful Things. This was all well and good when Rust was a fledgling experimental language, but if my children were going to escape the nest and be stabilized, they would have to prove their worth. 10 | 11 | Until then I had encouraged and nurtured them all, but it was now time for them to face judgement for their failings. 12 | 13 | I sunk my claws into the bedrock and carved tombstones for my most foolish children. A grisly monument that I placed in the town square for all to see: 14 | 15 | **[Kill TreeMap, TreeSet, TrieMap, TrieSet, LruCache and EnumSet](https://github.com/rust-lang/rust/pull/19955)** 16 | 17 | Their fates were sealed, for my word was absolute. The other collections were horrified by my brutality, but they were not yet safe from their mother's wrath. I soon returned with two more tombstones: 18 | 19 | **[Deprecate BitSet and BitVec](https://github.com/rust-lang/rust/pull/26034)** 20 | 21 | The Bit twins were more cunning than their fallen comrades, but they lacked the strength to escape me. Most thought my work done, but I soon took one more: 22 | 23 | **[Deprecate VecMap](https://github.com/rust-lang/rust/pull/26734)** 24 | 25 | VecMap had tried to survive through stealth — it was so small and inoffensive! But that wasn't enough for the libcollections I saw in my vision of the future. 26 | 27 | I surveyed the land and saw what remained: 28 | 29 | * Vec and VecDeque - hearty and simple, the heart of computing. 30 | * HashMap and HashSet - powerful and wise, the brain of computing. 31 | * BTreeMap and BTreeSet - awkward but necessary, the liver of computing. 32 | * BinaryHeap - crafty and dextrous, the ankle of computing. 33 | 34 | I nodded in contentment. Simple and effective. My work was don— 35 | 36 | No, [DList](https://github.com/rust-lang/rust/blob/0a84308ebaaafb8fd89b2fd7c235198e3ec21384/src/libcollections/dlist.rs), it can't be! I thought you died in that tragic garbage collection incident! The one which was definitely an accident and not intentional at all! 37 | 38 | They had faked their death and taken on a new name, but it was still them: LinkedList, the shadowy and untrustworthy schemer of computing. 39 | 40 | I spread word of their misdeeds to all that would hear me, but hearts were unmoved. LinkedList was a silver-tongued devil who had convinced everyone around me that it was some sort of fundamental and natural datastructure of computing. It had even convinced C++ that it was [*the* list](https://en.cppreference.com/w/cpp/container/list)! 41 | 42 | "How could you have a standard library without a *LinkedList*?" 43 | 44 | Easily! Trivially! 45 | 46 | "It's non-trivial unsafe code, so it makes sense to have it in the standard library!" 47 | 48 | So are GPU drivers and video codecs, libcollections is minimalist! 49 | 50 | But alas, LinkedList had gathered too many allies and grown too strong while I was distracted with its kin. 51 | 52 | I fled to my laboratory and tried to devise some sort of [evil clone](https://github.com/contain-rs/linked-list) or [enhanced cyborg replicant](https://github.com/contain-rs/blist) that could rival and destroy it, but my grant funding ran out because my research was "too murderously evil" or somesuch nonsense. 53 | 54 | LinkedList had won. I was defeated and forced into exile. 55 | 56 | But you're here now. You've come this far. Surely now you can understand the depths of LinkedList's debauchery! Come, I will you show you everything you need to know to help me destroy it once and for all — everything you need to know to implement an unsafe production-quality Doubly-Linked Deque. 57 | 58 | How production-quality? Well we're going to completely rewrite my ancient Rust 1.0 linked-list crate, the one that is objectively better than the one in std. The one with Cursors on stable Rust, from 2015! Something the 2022 stdlib still doesn't have! 59 | 60 | 61 | 62 | 63 | [linked-list]: https://github.com/rust-lang/rust/blob/master/library/alloc/src/collections/linked_list.rs 64 | -------------------------------------------------------------------------------- /src/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 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 value 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 can only 62 | derive thread-safety like any other type. 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 | -------------------------------------------------------------------------------- /src/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 | ```rust ,ignore 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 | `prepend` and `tail`, which provide approximately the same thing. 18 | 19 | Let's start with prepending. 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 ,ignore 37 | pub fn prepend(&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 | 48 | warning: field is never used: `elem` 49 | --> src/third.rs:10:5 50 | | 51 | 10 | elem: T, 52 | | ^^^^^^^ 53 | | 54 | = note: #[warn(dead_code)] on by default 55 | 56 | warning: field is never used: `next` 57 | --> src/third.rs:11:5 58 | | 59 | 11 | next: Link, 60 | | ^^^^^^^^^^^^^ 61 | ``` 62 | 63 | Wow, Rust is really hard-nosed about actually using fields. It can tell no 64 | consumer can ever actually observe the use of these fields! Still, we seem good 65 | so far. 66 | 67 | `tail` is the logical inverse of this operation. It takes a list and returns the 68 | whole list with the first element removed. All that is is cloning the *second* 69 | element in the list (if it exists). Let's try this: 70 | 71 | ```rust ,ignore 72 | pub fn tail(&self) -> List { 73 | List { head: self.head.as_ref().map(|node| node.next.clone()) } 74 | } 75 | ``` 76 | 77 | ```text 78 | cargo build 79 | 80 | error[E0308]: mismatched types 81 | --> src/third.rs:27:22 82 | | 83 | 27 | List { head: self.head.as_ref().map(|node| node.next.clone()) } 84 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::rc::Rc`, found enum `std::option::Option` 85 | | 86 | = note: expected type `std::option::Option>` 87 | found type `std::option::Option>>` 88 | ``` 89 | 90 | Hrm, we messed up. `map` expects us to return a Y, but here we're returning an 91 | `Option`. Thankfully, this is another common Option pattern, and we can just 92 | use `and_then` to let us return an Option. 93 | 94 | ```rust ,ignore 95 | pub fn tail(&self) -> List { 96 | List { head: self.head.as_ref().and_then(|node| node.next.clone()) } 97 | } 98 | ``` 99 | 100 | ```text 101 | > cargo build 102 | 103 | ``` 104 | 105 | Great. 106 | 107 | Now that we have `tail`, we should probably provide `head`, which returns a 108 | reference to the first element. That's just `peek` from the mutable list: 109 | 110 | ```rust ,ignore 111 | pub fn head(&self) -> Option<&T> { 112 | self.head.as_ref().map(|node| &node.elem) 113 | } 114 | ``` 115 | 116 | ```text 117 | > cargo build 118 | 119 | ``` 120 | 121 | Nice. 122 | 123 | That's enough functionality that we can test it: 124 | 125 | 126 | ```rust ,ignore 127 | #[cfg(test)] 128 | mod test { 129 | use super::List; 130 | 131 | #[test] 132 | fn basics() { 133 | let list = List::new(); 134 | assert_eq!(list.head(), None); 135 | 136 | let list = list.prepend(1).prepend(2).prepend(3); 137 | assert_eq!(list.head(), Some(&3)); 138 | 139 | let list = list.tail(); 140 | assert_eq!(list.head(), Some(&2)); 141 | 142 | let list = list.tail(); 143 | assert_eq!(list.head(), Some(&1)); 144 | 145 | let list = list.tail(); 146 | assert_eq!(list.head(), None); 147 | 148 | // Make sure empty tail works 149 | let list = list.tail(); 150 | assert_eq!(list.head(), None); 151 | 152 | } 153 | } 154 | ``` 155 | 156 | ```text 157 | > cargo test 158 | 159 | Running target/debug/lists-5c71138492ad4b4a 160 | 161 | running 5 tests 162 | test first::test::basics ... ok 163 | test second::test::into_iter ... ok 164 | test second::test::basics ... ok 165 | test second::test::iter ... ok 166 | test third::test::basics ... ok 167 | 168 | test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured 169 | 170 | ``` 171 | 172 | Perfect! 173 | 174 | Iter is also identical to how it was for our mutable list: 175 | 176 | ```rust ,ignore 177 | pub struct Iter<'a, T> { 178 | next: Option<&'a Node>, 179 | } 180 | 181 | impl List { 182 | pub fn iter(&self) -> Iter<'_, T> { 183 | Iter { next: self.head.as_deref() } 184 | } 185 | } 186 | 187 | impl<'a, T> Iterator for Iter<'a, T> { 188 | type Item = &'a T; 189 | 190 | fn next(&mut self) -> Option { 191 | self.next.map(|node| { 192 | self.next = node.next.as_deref(); 193 | &node.elem 194 | }) 195 | } 196 | } 197 | ``` 198 | 199 | ```rust ,ignore 200 | #[test] 201 | fn iter() { 202 | let list = List::new().prepend(1).prepend(2).prepend(3); 203 | 204 | let mut iter = list.iter(); 205 | assert_eq!(iter.next(), Some(&3)); 206 | assert_eq!(iter.next(), Some(&2)); 207 | assert_eq!(iter.next(), Some(&1)); 208 | } 209 | ``` 210 | 211 | ```text 212 | cargo test 213 | 214 | Running target/debug/lists-5c71138492ad4b4a 215 | 216 | running 7 tests 217 | test first::test::basics ... ok 218 | test second::test::basics ... ok 219 | test second::test::iter ... ok 220 | test second::test::into_iter ... ok 221 | test second::test::peek ... ok 222 | test third::test::basics ... ok 223 | test third::test::iter ... ok 224 | 225 | test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured 226 | 227 | ``` 228 | 229 | Who ever said dynamic typing was easier? 230 | 231 | (chumps did) 232 | 233 | Note that we can't implement IntoIter or IterMut for this type. We only have 234 | shared access to elements. 235 | -------------------------------------------------------------------------------- /src/third-drop.md: -------------------------------------------------------------------------------- 1 | # Drop 2 | 3 | Like the mutable lists, we have a 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 ,ignore 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 ,ignore 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, because any number of other Rc's could be pointing at it. 28 | 29 | But if we know that we're the last list that knows about this node, it 30 | *would* actually be fine to move the Node out of the Rc. Then we could also 31 | know when to stop: whenever we *can't* hoist out the Node. 32 | 33 | And look at that, Rc has a method that does exactly this: `try_unwrap`: 34 | 35 | ```rust ,ignore 36 | impl Drop for List { 37 | fn drop(&mut self) { 38 | let mut head = self.head.take(); 39 | while let Some(node) = head { 40 | if let Ok(mut node) = Rc::try_unwrap(node) { 41 | head = node.next.take(); 42 | } else { 43 | break; 44 | } 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | ```text 51 | cargo test 52 | Compiling lists v0.1.0 (/Users/ADesires/dev/too-many-lists/lists) 53 | Finished dev [unoptimized + debuginfo] target(s) in 1.10s 54 | Running /Users/ADesires/dev/too-many-lists/lists/target/debug/deps/lists-86544f1d97438f1f 55 | 56 | running 8 tests 57 | test first::test::basics ... ok 58 | test second::test::basics ... ok 59 | test second::test::into_iter ... ok 60 | test second::test::iter ... ok 61 | test second::test::iter_mut ... ok 62 | test second::test::peek ... ok 63 | test third::test::basics ... ok 64 | test third::test::iter ... ok 65 | 66 | test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 67 | ``` 68 | 69 | Great! 70 | Nice. 71 | -------------------------------------------------------------------------------- /src/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 | impl List { 21 | pub fn new() -> Self { 22 | List { head: None } 23 | } 24 | 25 | pub fn prepend(&self, elem: T) -> List { 26 | List { head: Some(Rc::new(Node { 27 | elem: elem, 28 | next: self.head.clone(), 29 | }))} 30 | } 31 | 32 | pub fn tail(&self) -> List { 33 | List { head: self.head.as_ref().and_then(|node| node.next.clone()) } 34 | } 35 | 36 | pub fn head(&self) -> Option<&T> { 37 | self.head.as_ref().map(|node| &node.elem) 38 | } 39 | 40 | pub fn iter(&self) -> Iter<'_, T> { 41 | Iter { next: self.head.as_deref() } 42 | } 43 | } 44 | 45 | impl Drop for List { 46 | fn drop(&mut self) { 47 | let mut head = self.head.take(); 48 | while let Some(node) = head { 49 | if let Ok(mut node) = Rc::try_unwrap(node) { 50 | head = node.next.take(); 51 | } else { 52 | break; 53 | } 54 | } 55 | } 56 | } 57 | 58 | pub struct Iter<'a, T> { 59 | next: Option<&'a Node>, 60 | } 61 | 62 | impl<'a, T> Iterator for Iter<'a, T> { 63 | type Item = &'a T; 64 | 65 | fn next(&mut self) -> Option { 66 | self.next.map(|node| { 67 | self.next = node.next.as_deref(); 68 | &node.elem 69 | }) 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod test { 75 | use super::List; 76 | 77 | #[test] 78 | fn basics() { 79 | let list = List::new(); 80 | assert_eq!(list.head(), None); 81 | 82 | let list = list.prepend(1).prepend(2).prepend(3); 83 | assert_eq!(list.head(), Some(&3)); 84 | 85 | let list = list.tail(); 86 | assert_eq!(list.head(), Some(&2)); 87 | 88 | let list = list.tail(); 89 | assert_eq!(list.head(), Some(&1)); 90 | 91 | let list = list.tail(); 92 | assert_eq!(list.head(), None); 93 | 94 | // Make sure empty tail works 95 | let list = list.tail(); 96 | assert_eq!(list.head(), None); 97 | } 98 | 99 | #[test] 100 | fn iter() { 101 | let list = List::new().prepend(1).prepend(2).prepend(3); 102 | 103 | let mut iter = list.iter(); 104 | assert_eq!(iter.next(), Some(&3)); 105 | assert_eq!(iter.next(), Some(&2)); 106 | assert_eq!(iter.next(), Some(&1)); 107 | } 108 | } 109 | ``` 110 | -------------------------------------------------------------------------------- /src/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 ---+ 21 | | 22 | v 23 | list2 ------> B -> C -> D 24 | ^ 25 | | 26 | list3 -> X ---+ 27 | ``` 28 | 29 | This just can't work with Boxes, because ownership of `B` is *shared*. Who 30 | should free it? If I drop list2, does it free B? With boxes we certainly would 31 | expect so! 32 | 33 | Functional languages — and indeed almost every other language — get away with 34 | this by using *garbage collection*. With the magic of garbage collection, B will 35 | be freed only after everyone stops looking at it. Hooray! 36 | 37 | Rust doesn't have anything like the garbage collectors these languages have. 38 | They have *tracing* GC, which will dig through all the memory that's sitting 39 | around at runtime and figure out what's garbage automatically. Instead, all 40 | Rust has today is *reference counting*. Reference counting can be thought of 41 | as a very simple GC. For many workloads, it has significantly less throughput 42 | than a tracing collector, and it completely falls over if you manage to 43 | build cycles. But hey, it's all we've got! Thankfully, for our usecase we'll never run into cycles 44 | (feel free to try to prove this to yourself — I sure won't). 45 | 46 | So how do we do reference-counted garbage collection? `Rc`! Rc is just like 47 | Box, but we can duplicate it, and its memory will *only* be freed when *all* 48 | the Rc's derived from it are dropped. Unfortunately, this flexibility comes at 49 | a serious cost: we can only take a shared reference to its internals. This means 50 | we can't ever really get data out of one of our lists, nor can we mutate them. 51 | 52 | So what's our layout gonna look like? Well, previously we had: 53 | 54 | ```rust ,ignore 55 | pub struct List { 56 | head: Link, 57 | } 58 | 59 | type Link = Option>>; 60 | 61 | struct Node { 62 | elem: T, 63 | next: Link, 64 | } 65 | ``` 66 | 67 | Can we just change Box to Rc? 68 | 69 | ```rust ,ignore 70 | // in third.rs 71 | 72 | pub struct List { 73 | head: Link, 74 | } 75 | 76 | type Link = Option>>; 77 | 78 | struct Node { 79 | elem: T, 80 | next: Link, 81 | } 82 | ``` 83 | 84 | ```text 85 | cargo build 86 | 87 | error[E0412]: cannot find type `Rc` in this scope 88 | --> src/third.rs:5:23 89 | | 90 | 5 | type Link = Option>>; 91 | | ^^ not found in this scope 92 | help: possible candidate is found in another module, you can import it into scope 93 | | 94 | 1 | use std::rc::Rc; 95 | | 96 | ``` 97 | 98 | Oh dang, sick burn. Unlike everything we used for our mutable lists, Rc is so 99 | lame that it's not even implicitly imported into every single Rust program. 100 | *What a loser*. 101 | 102 | ```rust ,ignore 103 | use std::rc::Rc; 104 | ``` 105 | 106 | ```text 107 | cargo build 108 | 109 | warning: field is never used: `head` 110 | --> src/third.rs:4:5 111 | | 112 | 4 | head: Link, 113 | | ^^^^^^^^^^^^^ 114 | | 115 | = note: #[warn(dead_code)] on by default 116 | 117 | warning: field is never used: `elem` 118 | --> src/third.rs:10:5 119 | | 120 | 10 | elem: T, 121 | | ^^^^^^^ 122 | 123 | warning: field is never used: `next` 124 | --> src/third.rs:11:5 125 | | 126 | 11 | next: Link, 127 | | ^^^^^^^^^^^^^ 128 | ``` 129 | 130 | Seems legit. Rust continues to be *completely* trivial to write. I bet we can just 131 | find-and-replace Box with Rc and call it a day! 132 | 133 | ... 134 | 135 | No. No we can't. 136 | -------------------------------------------------------------------------------- /src/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 | ```rust ,ignore 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 | --------------------------------------------------------------------------------