Learn Rust With Entirely Too Many Linked Lists
42

Filling In Random Bits

A real collection in the std-collections style implements a long tail of traits: Default, Clone, Extend, FromIterator, Debug, PartialEq/Eq, PartialOrd/Ord, Hash, plus the simple is_empty, len, clear methods. Most of them are one-liners on top of iter().

  1. Trivial methods first. len and is_empty are direct reads. clear is *self = Self::new() after Drop-equivalent work — easiest is to drop in place via mem::take-of-self isn't possible without Default, so just loop pop_front.
    impl<T> LinkedList<T> {
        pub fn len(&self) -> usize { self.len }
        pub fn is_empty(&self) -> bool { self.len == 0 }
        pub fn clear(&mut self) {
            while self.pop_front().is_some() {}
        }
    }
    
    impl<T> Default for LinkedList<T> {
        fn default() -> Self { LinkedList::new() }
    }
  2. Extend and FromIterator: take an iterator, push each element onto the back. FromIterator builds a new list and forwards to Extend. Both are short because push_back does the real work.
    impl<T> Extend<T> for LinkedList<T> {
        fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
            for item in iter { self.push_back(item); }
        }
    }
    
    impl<T> FromIterator<T> for LinkedList<T> {
        fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
            let mut list = LinkedList::new();
            list.extend(iter);
            list
        }
    }
  3. Comparison and hashing all delegate to iterating both lists in lockstep. PartialEq walks element-wise; PartialOrd/Ord use lexicographic order; Hash mixes in the length followed by every element. Debug uses the formatter's debug_list helper.
    use std::fmt;
    use std::hash::{Hash, Hasher};
    
    impl<T: fmt::Debug> fmt::Debug for LinkedList<T> {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            f.debug_list().entries(self).finish()
        }
    }
    
    impl<T: PartialEq> PartialEq for LinkedList<T> {
        fn eq(&self, other: &Self) -> bool {
            self.len == other.len && self.iter().eq(other.iter())
        }
    }
    impl<T: Eq> Eq for LinkedList<T> {}
    
    impl<T: PartialOrd> PartialOrd for LinkedList<T> {
        fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
            self.iter().partial_cmp(other.iter())
        }
    }
    impl<T: Ord> Ord for LinkedList<T> {
        fn cmp(&self, other: &Self) -> std::cmp::Ordering {
            self.iter().cmp(other.iter())
        }
    }
    
    impl<T: Hash> Hash for LinkedList<T> {
        fn hash<H: Hasher>(&self, state: &mut H) {
            self.len.hash(state);
            for item in self { item.hash(state); }
        }
    }