run_on_drop/lib.rs
1//! This crate provides a closure wrapper that will run the closure when it is dropped.
2//!
3//! See [`on_drop`] for examples.
4
5#![no_std]
6
7#[cfg(test)]
8extern crate alloc;
9
10use core::mem::ManuallyDrop;
11
12/// Creates an object that will run the closure when it is dropped.
13///
14/// The closure will run even if the object is dropped during unwinding.
15///
16/// Running the closure can be cancelled by calling [`OnDrop::forget`].
17///
18/// # Example: Object setup without RAII
19///
20/// ```
21/// use run_on_drop::on_drop;
22///
23/// let object = create_object();
24/// let cleanup = on_drop(|| destroy_object(&object));
25/// initialize_object(&object); // might unwind
26/// cleanup.forget();
27/// return object;
28/// #
29/// # fn create_object() { }
30/// # fn destroy_object(_: &()) { }
31/// # fn initialize_object(_: &()) { }
32/// ```
33///
34/// # Example: Resetting a flag
35///
36/// ```
37/// use core::cell::Cell;
38/// use run_on_drop::on_drop;
39///
40/// let flag = Cell::new(false);
41///
42/// flag.set(true);
43/// let _reset_flag = on_drop(|| flag.set(false));
44/// f();
45/// #
46/// # fn f() { }
47/// ```
48#[inline(always)]
49pub fn on_drop<F>(f: F) -> OnDrop<F>
50where
51 F: FnOnce(),
52{
53 OnDrop(ManuallyDrop::new(f))
54}
55
56/// A type that runs the contained closure when it is dropped.
57///
58/// This type is constructed with the [`on_drop`] function.
59pub struct OnDrop<F>(ManuallyDrop<F>)
60where
61 F: FnOnce();
62
63// SAFETY: OnDrop does not provide shared access to the `F`.
64unsafe impl<F> Sync for OnDrop<F> where F: FnOnce() {}
65
66impl<F> OnDrop<F>
67where
68 F: FnOnce(),
69{
70 /// Forgets this `OnDrop` without leaking memory.
71 ///
72 /// The contained closure will be dropped without being run.
73 #[inline(always)]
74 pub fn forget(self) {
75 let mut slf = ManuallyDrop::new(self);
76 // SAFETY: Since slf is ManuallyDrop, the drop impl will not run.
77 let _f = unsafe { ManuallyDrop::take(&mut slf.0) };
78 }
79}
80
81impl<F> Drop for OnDrop<F>
82where
83 F: FnOnce(),
84{
85 #[inline(always)]
86 fn drop(&mut self) {
87 // SAFETY: This is the drop impl so no other code will access self.0.
88 let f = unsafe { ManuallyDrop::take(&mut self.0) };
89 f();
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use {crate::on_drop, alloc::boxed::Box, core::cell::Cell};
96
97 #[test]
98 fn drop() {
99 let mut dropped = Box::new(0);
100 {
101 on_drop(|| *dropped += 1);
102 }
103 assert_eq!(*dropped, 1);
104 }
105
106 #[test]
107 fn forget() {
108 let mut dropped = Box::new(0);
109 {
110 on_drop(|| *dropped += 1).forget();
111 }
112 assert_eq!(*dropped, 0);
113 }
114
115 #[test]
116 fn double_drop() {
117 let dropped = Box::new(Cell::new(0));
118 {
119 let f = Box::new(on_drop(|| dropped.set(dropped.get() + 1)));
120 let dropped = &dropped;
121 on_drop(move || {
122 let _v = f;
123 dropped.set(dropped.get() + 1);
124 });
125 }
126 assert_eq!(dropped.get(), 2);
127 }
128
129 #[test]
130 fn double_drop_forget() {
131 let dropped = Box::new(Cell::new(0));
132 {
133 let f = Box::new(on_drop(|| dropped.set(dropped.get() + 1)));
134 on_drop(move || {
135 let _v = f;
136 })
137 .forget();
138 }
139 assert_eq!(dropped.get(), 1);
140 }
141
142 #[test]
143 #[should_panic(expected = "explicit panic")]
144 fn panic_in_double_drop_forget() {
145 let dropped = Box::new(Cell::new(0));
146 let _assert_n = on_drop(|| assert_eq!(dropped.get(), 0));
147 {
148 let f = Box::new(on_drop(|| panic!()));
149 let dropped = &dropped;
150 on_drop(move || {
151 let _v = f;
152 dropped.set(dropped.get() + 1);
153 })
154 .forget();
155 }
156 }
157}