-
Notifications
You must be signed in to change notification settings - Fork 22
Description
Proposal
Problem statement
Sometimes I want to run a function upon the end of a function or block. This may be for resource clean-up or just any side effect. This function does not necessarily own data. Or it might reference data with interior mutability.
We want a straightforward and idiomatic alternative to golang's defer
.
Motivating examples or use cases
thread_local! {
static RECURSION_COUNT: Cell<u32> = Cell::new(0);
}
fn my_function() {
RECURSION_COUNT.update(|n| n + 1);
let _guard = OnDrop::new(|| RECURSION_COUNT.update(|n| n - 1));
println!("my_function is being executed {} times", RECURSION_COUNT.get());
// insert any complex logic here, with early returns, recursion and what have you
}
Solution sketch
struct OnDrop<F: FnOnce()>(ManuallyDrop<F>);
impl<F: FnOnce()> OnDrop<F> {
fn new(f: F) -> Self {
OnDrop(ManuallyDrop::new(f))
}
fn into_inner(self) -> F;
}
impl<F: FnOnce()> Drop for OnDrop<F> {
fn drop(&mut self) {
let f = unsafe { ManuallyDrop::take(&mut self.0) };
f();
}
}
Alternatives
One alternative is to add another constructor function to WithDrop
with no data.
let _guard = WithDrop::from_fn(|| todo!());
/// ...is equivalent to...
let _guard = WithDrop::new((), |_| todo!());
I would say this feels like deciding not to have HashSet
because we already have HashMap
. It would be awkward to write the signature WithDrop<(), ...>
. A key feature of WithDrop
is that it wraps a value, and this feels like a distinct use case.
I estimate that this utility is about equally as useful as WithDrop
. So, at the risk of a slippy slope fallacy, I think if WithDrop
is accepted then this should be too.