35
Extras
With the layout right, the rest is mechanical: peek_front, peek_back, is_empty, an Iter and IterMut. Most are one-line unsafe blocks that produce a short-lived reference from a raw pointer.
- Peeks: deref the head/tail raw pointer once and return
Option<&T>. The lifetime of the returned reference is tied to&selfby the elision rules, so the borrow checker enforces that the queue isn't mutated while the peek is live.pub fn peek_front(&self) -> Option<&T> { unsafe { self.head.as_ref().map(|n| &n.elem) } } pub fn peek_back(&self) -> Option<&T> { unsafe { self.tail.as_ref().map(|n| &n.elem) } } pub fn is_empty(&self) -> bool { self.head.is_null() } -
Iterholds a*const Node<T>cursor and aPhantomData<&'a T>to bind the lifetime.nextreads through the cursor, returns&'a T, advances.IterMutis the*mutanalog. Both are short, both pass Miri.use std::marker::PhantomData; pub struct Iter<'a, T> { next: *const Node<T>, _marker: PhantomData<&'a T>, } impl<T> List<T> { pub fn iter(&self) -> Iter<'_, T> { Iter { next: self.head, _marker: PhantomData } } } impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { unsafe { self.next.as_ref().map(|node| { self.next = node.next; &node.elem }) } } } -
IntoIterconsumes the queue — wrap the list and yield viapop_front. Drop on the wrapper drains the rest. Nounsafeneeded in the iterator itself because all the pointer work is hidden insidepop_front.pub struct IntoIter<T>(List<T>); impl<T> List<T> { pub fn into_iter(self) -> IntoIter<T> { IntoIter(self) } } impl<T> Iterator for IntoIter<T> { type Item = T; fn next(&mut self) -> Option<T> { self.0.pop_front() } }