Dropping linked list without growing stack
⚓ Rust 📅 2025-12-29 👤 surdeus 👁️ 1This can be implemented with tail calls (unstable feature as of now) but with some difficulty. Presented is the full solution.
#![expect(incomplete_features)]
#![feature(explicit_tail_calls)]
struct ScreamOnDrop(i32);
impl Drop for ScreamOnDrop {
fn drop(&mut self) {
println!("Dropping #{}", self.0);
}
}
// The list is actually defined as usual.
struct LinkedList {
now: ScreamOnDrop,
// any data fields can be added here...
next: Option<Box<LinkedList>>
}
impl LinkedList {
// Now, we get to something unusual.
// We are going to tail call dropping the next list element.
// It must have signature of Fn(LinkedList) -> ().
// Therefore, we cannot call it from anything but Fn(LinkedList) -> ();
// this mandates a `Self::drop` inherent function.
fn drop(mut self) {
if let Some(tail) = self.next.take() {
// We got a Box<LinkedList>. It does not have inherent `drop` so
// we must dereference it.
let tail: LinkedList = *tail;
// Before the call starts, every other field in `self` is dropped.
become tail.drop()
}
// else { panic!("uncomment to see that stack does not grow") }
}
}
// We need to route Drop implementation to the inherent function.
// `Drop::drop` only gives us a mutable reference, so only elements from
// the 2nd onwards will be dropped through inherent `LinkedList::drop`.
impl Drop for LinkedList {
fn drop(&mut self) {
// Checking if there is any list tail.
// Other fields will be handled by the compiler automatically.
if let Some(tail) = self.next.take() {
(*tail).drop(); // tail call conditions are not met here
}
}
}
fn main() {
let mut list = None;
for i in 0..5 {
let next_node = LinkedList { now: ScreamOnDrop(5 - i), next: list };
list = Some(Box::new(next_node));
}
}
It has somewhat unusual drop order:
Dropping #1
Dropping #2
Dropping #3
Dropping #4
Dropping #0
3 posts - 3 participants
🏷️ Rust_feed