Is a local variable dropped before a non-returning function is called?

use core::mem::ManuallyDrop;

pub struct OnDrop<T, F>
where
    F: FnOnce(),
{
    value: ManuallyDrop<T>,
    callback: ManuallyDrop<F>,
}

impl<T, F> OnDrop<T, F>
where
    F: FnOnce(),
{
    pub fn new(value: T, callback: F) -> Self {
        Self {
            value: ManuallyDrop::new(value),
            callback: ManuallyDrop::new(callback),
        }
    }
}

impl<T, F> Drop for OnDrop<T, F>
where
    F: FnOnce(),
{
    fn drop(&mut self) {
        unsafe {
            ManuallyDrop::<T>::drop(&mut self.value);
            (ManuallyDrop::<F>::take(&mut self.callback))();
        }
    }
}

fn never_returns() -> ! {
    loop {}
}

fn main() {
    println!("starting ...");

    let x = OnDrop::new(String::from("Hello"), || println!("string got dropped"));
    never_returns();
}

(Playground)

Why is a local variable not being dropped before calling a non-returning function? Do I need to call drop on every value that should be called before a non-returning function?

  • This would be an extra rule in the automatic drop behavior, increasing complexity of the implicit parts of the language. Such complexity creates surprises for users.
  • The non-returning function might be intended to execute in context of a guard value, such as tracing::Span::enter(), even if it doesn't explicitly use that value.

Only if it's needed for your application. And if it is, I would suggest creating a block rather than using drop() calls; this is more robust against changes to the code that introduce more variables.

fn main() {
    {
        println!("starting ...");
        let x = OnDrop::new(String::from("Hello"), || println!("string got dropped"));
    }
    never_returns();
}
5 Likes

It would also be a change in semantics to change drop order in thise case now, obviously.

But note also that calling a non-returning function doesn't mean locals still alive on the call stack will definitely never drop today. They can still get dropped via unwinding panic.

1 Like

It's maybe worth noting that this does prevent the use of tail call optimization techniques. The reserved become keyword is thus planned to modify drop timing to happen before the tail call (if/when it gets implemented).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.