//! Small library for `defer`ing the running of function until the end of a block. //! //! # Uasge //! Similar to the `defer` mechanism in Go, we can use this to defer the calling of functions //! ``` //! fn do_something() //! { //! let _guard = phantomdrop::defer(|| println!("Hello!")); //! // do some work //! } // "Hello!" will now be printed when the function returns or unwinds (unless unwinds are disabled). //! ``` //! //! # Values //! //! The guard can also hold a value //! ``` //! fn do_something(print: String) //! { //! # use phantomdrop::PhantomDrop; //! let _guard = PhantomDrop::new(print, |string| println!("Dropped: {}", string)); //! // do some work //! } // `print` will now be printed here. //! ``` //! //! Or capture a value, by reference, mutable reference, or moving. //! ``` //! fn do_something(print: String) //! { //! let _guard = phantomdrop::defer(move || println!("Dropped: {}", print)); // Moves `print` into itself. //! // do some work //! } // `print` will now be printed here. //! //! fn do_something_by_reference(print: String) //! { //! let _guard = phantomdrop::defer(|| println!("Dropped: {}", print)); // Holds an immutable reference to `print`. //! let trimmed = print.trim(); // Can still be used //! } // `print` will now be printed here. //! //! fn do_something_by_mutable_reference(print: &mut String) //! { //! let _guard = phantomdrop::defer(|| *print = String::from("Dropped")); // Holds a mutable reference to `print`. //! } // `print` will now be set to "Dropped" here. //! ``` use core::mem::MaybeUninit; use core::ops::Drop; /// When dropped, the included function is ran with the argument held by the structure. /// /// # Notes /// If both the function and the value are zero-sized (unique non-capturing closures are ZSTs), this wrapper will also be zero-sized. #[derive(Debug)] pub struct PhantomDrop(MaybeUninit<(T, F)>); /// `PhantomDrop` with no associated data. pub type PhantomDropEmpty = PhantomDrop<(), F>; /// `PhantomDrop` that does not capture. pub type PhantomDropPure = PhantomDrop; impl Clone for PhantomDrop { #[inline] fn clone(&self) -> Self { let re = unsafe { self.value_ref() }; Self(MaybeUninit::new((re.0.clone(), re.1.clone()))) } } impl PhantomDrop<(),F> where F: FnOnce(()) { /// Defer a function to run when this guard is dropped. #[inline] pub fn defer(fun: F) -> Self { PhantomDrop::new((), fun) } } /// Defer this function to run when the returned guard is dropped. pub fn defer(fun: impl FnOnce()) -> PhantomDrop<(), impl FnOnce(())> { PhantomDrop::defer(move |_| fun()) } impl PhantomDrop where F: FnOnce(T) { #[inline(always)] unsafe fn value_mut(&mut self) -> &mut (T, F) { &mut (*self.0.as_mut_ptr()) } #[inline(always)] unsafe fn value_ref(&self) -> &(T, F) { &(*self.0.as_ptr()) } #[inline(always)] unsafe fn into_raw_parts(self) -> (T, F) { let (v, f) = self.0.as_ptr().read(); core::mem::forget(self); (v, f) } /// Defer a function to run on this stored value when this guard is #[inline] pub fn new(value: T, fun: F) -> Self { Self(MaybeUninit::new((value, fun))) } /// Consume the instance into its held type without running the drop closure. #[inline] pub fn into_inner(self) -> T { unsafe { self.into_raw_parts() }.0 } /// Consume this instance without running the drop closure. /// /// # Notes /// This largely has the same behaviour of `core::mem::forget`, however this method is preferable for instances of `PhantomDrop`, as it properly calls destructors for both its value and its function if needed. #[inline] pub fn forget(self) { unsafe { self.into_raw_parts() }; } /// Get a mutable reference to the held type. #[inline] pub fn as_mut(&mut self) -> &mut T { unsafe { &mut self.value_mut().0 } } /// Get a reference to the held type. #[inline] pub fn as_ref(&self) -> &T { unsafe { &self.value_ref().0 } } /// Replace the function to be ran on drop with a no-op. #[inline] pub fn neutralise(self) -> PhantomDrop { PhantomDrop::new(self.into_inner(), drop) } } impl PhantomDrop> { /// Box the closure in this instance on to the heap. #[inline] pub fn boxed(self) -> PhantomDrop> { let (v, f) = unsafe { self.into_raw_parts() }; PhantomDrop::new(v, Box::new(f)) } /// Replace the function to be ran on drop with a no-op in place on the heap. #[inline] pub fn neutralise_boxed(&mut self) { unsafe { self.value_mut().1 = Box::new(drop) }; } } impl PhantomDrop { /// Replace the function to be ran on drop with a no-op in place with no allocations. #[inline] pub fn neutralise_in_place(&mut self) { unsafe { self.value_mut().1 = drop }; } } impl Drop for PhantomDrop where F: FnOnce(T) { #[inline] fn drop(&mut self) { let (v, f) = unsafe { self.0.as_ptr().read() }; f(v); } } #[cfg(test)] mod tests { #[test] fn zero_sized() { let guard = super::defer(|| println!("Hello world!")); assert_eq!(core::mem::size_of_val(&guard), 0); } #[test] fn mut_reference_holding() { let mut hi = String::from("Hello?"); let _guard = super::PhantomDrop::new(&mut hi, |string| { *string = String::from("Hello!"); println!("{}", string); }); } #[test] fn reference_holding() { let hi = String::from("Hello!"); let _guard = super::PhantomDrop::new(&hi, |string| println!("{}", string)); } #[test] fn value_holding() { let hi = String::from("Hello!"); let _guard = super::PhantomDrop::new(hi, |string| println!("{}", string)); } #[test] fn value_capturing() { let hi = String::from("Hello!"); let _guard = super::defer(move || println!("{}", hi)); } #[test] fn mut_reference_capturing() { let mut hi = String::from("Hello?"); let _guard = super::defer(|| { hi = String::from("Hello!"); println!("{}", hi) }); } #[test] fn reference_capturing() { let hi = String::from("Hello!"); let _guard = super::defer(|| println!("{}", hi)); } #[test] fn deferring() { let _guard = super::defer(|| println!("Hello!")); } }