40
Panic Safety
Once T carries a destructor, every operation has to consider "what if a panic unwinds through the middle of me?" The bad outcome is leaving the list in a state where Drop will double-free, dangle, or skip a node. The standard tools: keep invariants per-step, or build on the side and swap atomically.
- The dangerous spot in
CloneisT::clone(). We want to clone every element and link the new nodes into a fresh list. The risk: clone the first three elements, panic on the fourth, and an unwindingDropfrees three half-linked nodes. The fix: build an entirely newLinkedList<T>withpush_back(which is itself panic-safe — only one mutation per call, in the right order) and only assign it to the destination at the very end.impl<T: Clone> Clone for LinkedList<T> { fn clone(&self) -> Self { let mut new = LinkedList::new(); for item in self { // if T::clone panics here, `new` is dropped // cleanly by its own Drop impl. self is untouched. new.push_back(item.clone()); } new } } - Inside each individual operation, the invariant to maintain is: at every point where a panic could fire, the list's
front/back/lenand every node'sfront/backagree with each other. Allocate first (allocations can panic on OOM in some configurations), then do the rewiring as a sequence of writes that never leaves the structure half-linked.// good: allocate first, rewire after. let new = NonNull::new_unchecked(Box::into_raw(Box::new(node))); // from here down, no panics possible. self.front = Some(new); self.len += 1; // bad pattern: rewire while dropping old data, then alloc. // if the alloc panics, len and pointers disagree. - Drop check meets panic safety: if a
Node'sTpanics during drop, the whole list'sDropimpl must still complete. OurDropcallspop_frontrepeatedly; if one element's destructor panics,pop_fronthas already removed it from the list, so subsequent drops still find a consistent structure. The double-panic case (panicking while unwinding) aborts the process — which is the correct behavior.// Drop loop: // iter N: // pop_front detaches node N from list (writes only) // pop_front returns Some(elem) // elem dropped at end of statement // -- if elem's drop panics, N is already gone from list, // list is still well-formed, unwind continues.