toolbox_rs/
linked_list.rs

1/// Simplified implementation of a linked list that is suitable for a textbook
2/// implementation of an LRU cache. The code below implements three important
3/// functions upon which the cache is built:
4///
5/// 1) push_front(): insert an element to the front of the list
6/// 2) pop_back(): remove the back element if it exists, and
7/// 3) move_to_front(): move an existing element to the front of the list
8///
9/// The implementation is modelled after the excellent writeup on implementing
10/// linked lists called "Learn Rust With Entirely Too Many Linked Lists" which
11/// can be found at https://rust-unofficial.github.io/too-many-lists/index.html
12///
13/// Since linked lists use pointers and are self-referential, there's a ton of
14/// unsafe code in the implementation. Please be mindful, when changing the code.
15///
16/// Once the standard library contains a stable implementation of linked lists
17/// with cursors this code could be removed. We are not there yet as of writing.
18use std::marker::PhantomData;
19use std::ptr::NonNull;
20
21pub struct LinkedList<T> {
22    front: Link<T>,
23    back: Link<T>,
24    len: usize,
25    _ghost: PhantomData<T>,
26}
27pub type ListCursor<T> = NonNull<Node<T>>;
28type Link<T> = Option<ListCursor<T>>;
29
30pub struct Node<T> {
31    next: Link<T>,
32    prev: Link<T>,
33    elem: T,
34}
35
36impl<T> LinkedList<T> {
37    pub fn new() -> Self {
38        Self {
39            front: None,
40            back: None,
41            len: 0,
42            _ghost: PhantomData,
43        }
44    }
45
46    pub fn push_front(&mut self, elem: T) -> ListCursor<T> {
47        // SAFETY: it's a linked-list, what do you want?
48        unsafe {
49            let new = NonNull::new_unchecked(Box::into_raw(Box::new(Node {
50                next: None,
51                prev: None,
52                elem,
53            })));
54            if let Some(old) = self.front {
55                // Put the new front before the old one
56                (*old.as_ptr()).next = Some(new);
57                (*new.as_ptr()).prev = Some(old);
58            } else {
59                // If there's no front, then we're the empty list and need
60                // to set the back too.
61                self.back = Some(new);
62            }
63            // These things always happen!
64            self.front = Some(new);
65            self.len += 1;
66
67            new
68        }
69    }
70
71    pub fn move_to_front(&mut self, b: &ListCursor<T>) {
72        if self.is_empty() {
73            return;
74        }
75
76        if let Some(front) = self.front {
77            // Is node B already in front?
78            if front == *b {
79                return;
80            }
81        }
82
83        // SAFETY: it's a linked-list, what do you want?
84        unsafe {
85            // cut node b from list by short-cutting a<->c
86            let a = (*b.as_ptr()).next;
87            (*a.unwrap().as_ptr()).prev = (*b.as_ptr()).prev;
88            if let Some(c) = (*b.as_ptr()).prev {
89                (*c.as_ptr()).next = (*b.as_ptr()).next;
90            }
91
92            // if the last element is moved to front, then update it with the next element in row
93            if self.back.unwrap() == *b {
94                debug_assert!((*b.as_ptr()).prev.is_none());
95                self.back = a;
96            }
97        }
98
99        // SAFETY: it's a linked-list, what do you want?
100        unsafe {
101            // move now-floating node b to the front of the linked list
102            let x = self.front;
103
104            (*b.as_ptr()).prev = x;
105            (*b.as_ptr()).next = None;
106            (*x.unwrap().as_ptr()).next = Some(*b);
107            self.front = Some(*b);
108        }
109    }
110
111    pub fn pop_back(&mut self) -> Option<T> {
112        unsafe {
113            // Only have to do stuff if there is a back node to pop.
114            self.back.map(|node| {
115                // Bring the Box front to life so we can move out its value and
116                // Drop it (Box continues to magically understand this for us).
117                let boxed_node = Box::from_raw(node.as_ptr());
118                let result = boxed_node.elem;
119
120                // Make the next node into the new back.
121                self.back = boxed_node.next;
122                if let Some(new) = self.back {
123                    // Cleanup its reference to the removed node
124                    (*new.as_ptr()).prev = None;
125                } else {
126                    // If the back is now null, then this list is now empty!
127                    self.front = None;
128                }
129
130                self.len -= 1;
131                result
132                // Box gets implicitly freed here, knows there is no T.
133            })
134        }
135    }
136
137    pub fn len(&self) -> usize {
138        self.len
139    }
140
141    pub fn is_empty(&self) -> bool {
142        self.len == 0
143    }
144
145    pub fn clear(&mut self) {
146        // Oh look it's drop again
147        while self.pop_back().is_some() {}
148    }
149
150    pub fn get_front(&self) -> &T {
151        // TODO: decide whether this returns a reference or a copy
152        unsafe { &self.front.unwrap().as_ref().elem }
153    }
154
155    pub fn get_front_mut(&mut self) -> &mut T {
156        // SAFETY: get_front() already ensures front exists and is valid
157        unsafe { &mut self.front.unwrap().as_mut().elem }
158    }
159}
160
161impl<T> Drop for LinkedList<T> {
162    fn drop(&mut self) {
163        self.clear()
164    }
165}
166
167impl<T> Default for LinkedList<T> {
168    fn default() -> Self {
169        Self::new()
170    }
171}
172
173#[cfg(test)]
174mod test {
175    use super::LinkedList;
176
177    #[test]
178    fn default_init_cursor_noop() {
179        let mut list = LinkedList::default();
180
181        assert_eq!(list.len(), 0);
182        assert_eq!(list.pop_back(), None);
183        assert_eq!(list.len(), 0);
184        let cursor = list.push_front(10);
185        assert_eq!(list.len(), 1);
186        assert_eq!(list.pop_back(), Some(10));
187        list.move_to_front(&cursor); // no-op since list is empty
188    }
189
190    #[test]
191    fn test_basic_front() {
192        let mut list = LinkedList::new();
193
194        assert_eq!(list.len(), 0);
195        assert_eq!(list.pop_back(), None);
196        assert_eq!(list.len(), 0);
197
198        list.push_front(10);
199        assert_eq!(list.len(), 1);
200        assert_eq!(list.pop_back(), Some(10));
201        assert_eq!(list.len(), 0);
202        assert_eq!(list.pop_back(), None);
203        assert_eq!(list.len(), 0);
204
205        list.push_front(10);
206        assert_eq!(list.len(), 1);
207        list.push_front(20);
208        assert_eq!(list.len(), 2);
209        list.push_front(30);
210        assert_eq!(list.len(), 3);
211        assert_eq!(list.pop_back(), Some(10));
212        assert_eq!(list.len(), 2);
213        list.push_front(40);
214        assert_eq!(list.len(), 3);
215        assert_eq!(list.pop_back(), Some(20));
216        assert_eq!(list.len(), 2);
217        assert_eq!(list.pop_back(), Some(30));
218        assert_eq!(list.len(), 1);
219        assert_eq!(list.pop_back(), Some(40));
220        assert_eq!(list.len(), 0);
221        assert_eq!(list.pop_back(), None);
222        assert_eq!(list.len(), 0);
223        assert_eq!(list.pop_back(), None);
224        assert_eq!(list.len(), 0);
225    }
226
227    #[test]
228    fn basic_move_to_front() {
229        let mut list = LinkedList::new();
230
231        assert_eq!(list.len(), 0);
232        let first_inserted = list.push_front(1);
233        list.move_to_front(&first_inserted);
234
235        list.push_front(5);
236        list.push_front(4);
237        list.push_front(3);
238        list.push_front(2);
239
240        list.move_to_front(&first_inserted);
241
242        list.push_front(0);
243
244        let mut result = Vec::new();
245        while let Some(element) = list.pop_back() {
246            result.push(element);
247        }
248
249        assert_eq!(result, vec![5, 4, 3, 2, 1, 0]);
250    }
251
252    #[test]
253    fn push_sort_move() {
254        // test idea:
255        // - nodes handles are stored in an array
256        // - sort array by element
257        // - run move_to_front on all elements in order of sorted array
258        // - output should be sorted
259        let mut list = LinkedList::new();
260        let mut handles = Vec::new();
261        assert_eq!(list.len(), 0);
262        handles.push(list.push_front(1));
263        handles.push(list.push_front(5));
264        handles.push(list.push_front(2));
265        handles.push(list.push_front(4));
266        handles.push(list.push_front(3));
267
268        handles.sort_by_key(|h| unsafe { h.as_ref().elem });
269
270        handles.iter().for_each(|handle| {
271            list.move_to_front(handle);
272        });
273
274        let mut result = Vec::new();
275        while let Some(element) = list.pop_back() {
276            result.push(element);
277        }
278
279        assert_eq!(result, vec![1, 2, 3, 4, 5]);
280    }
281
282    #[test]
283    #[should_panic]
284    fn test_get_front_mut_empty() {
285        let mut list: LinkedList<i32> = LinkedList::new();
286        let _result = list.get_front_mut(); // Should panic on empty list
287    }
288
289    #[test]
290    fn test_get_front_mut() {
291        let mut list = LinkedList::new();
292
293        // Test with one element
294        list.push_front(10);
295        {
296            let front = list.get_front_mut();
297            *front = 20;
298        }
299        assert_eq!(list.get_front(), &20);
300
301        // Test with multiple elements
302        list.push_front(30);
303        list.push_front(40);
304        {
305            let front = list.get_front_mut();
306            *front = 50;
307        }
308        assert_eq!(list.get_front(), &50);
309
310        // Verify other elements are unchanged
311        assert_eq!(list.pop_back(), Some(20));
312    }
313}