#![no_std]
#[cfg(test)]
extern crate alloc;
use core::mem::ManuallyDrop;
#[inline(always)]
pub fn on_drop<F>(f: F) -> OnDrop<F>
where
F: FnOnce(),
{
OnDrop(ManuallyDrop::new(f))
}
pub struct OnDrop<F>(ManuallyDrop<F>)
where
F: FnOnce();
unsafe impl<F> Sync for OnDrop<F> where F: FnOnce() {}
impl<F> OnDrop<F>
where
F: FnOnce(),
{
#[inline(always)]
pub fn forget(self) {
let mut slf = ManuallyDrop::new(self);
let _f = unsafe { ManuallyDrop::take(&mut slf.0) };
}
}
impl<F> Drop for OnDrop<F>
where
F: FnOnce(),
{
#[inline(always)]
fn drop(&mut self) {
let f = unsafe { ManuallyDrop::take(&mut self.0) };
f();
}
}
#[cfg(test)]
mod tests {
use {crate::on_drop, alloc::boxed::Box, core::cell::Cell};
#[test]
fn drop() {
let mut dropped = Box::new(0);
{
on_drop(|| *dropped += 1);
}
assert_eq!(*dropped, 1);
}
#[test]
fn forget() {
let mut dropped = Box::new(0);
{
on_drop(|| *dropped += 1).forget();
}
assert_eq!(*dropped, 0);
}
#[test]
fn double_drop() {
let dropped = Box::new(Cell::new(0));
{
let f = Box::new(on_drop(|| dropped.set(dropped.get() + 1)));
let dropped = &dropped;
on_drop(move || {
let _v = f;
dropped.set(dropped.get() + 1);
});
}
assert_eq!(dropped.get(), 2);
}
#[test]
fn double_drop_forget() {
let dropped = Box::new(Cell::new(0));
{
let f = Box::new(on_drop(|| dropped.set(dropped.get() + 1)));
on_drop(move || {
let _v = f;
})
.forget();
}
assert_eq!(dropped.get(), 1);
}
#[test]
#[should_panic(expected = "explicit panic")]
fn panic_in_double_drop_forget() {
let dropped = Box::new(Cell::new(0));
let _assert_n = on_drop(|| assert_eq!(dropped.get(), 0));
{
let f = Box::new(on_drop(|| panic!()));
let dropped = &dropped;
on_drop(move || {
let _v = f;
dropped.set(dropped.get() + 1);
})
.forget();
}
}
}