diff --git a/Cargo.toml b/Cargo.toml index fe6a944..0a9a7c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,10 @@ [package] name = "phantomdrop" -version = "0.1.0" +description = "Go-like deferring of function calls" +keywords = ["defer", "drop", "go"] +version = "1.0.0" authors = ["Avril "] edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +license = "GPL-3.0-or-later" [dependencies] diff --git a/src/lib.rs b/src/lib.rs index f3f3ec2..a99704f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,51 @@ //! 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). +//! ``` +//! +//! 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)>); @@ -25,6 +67,13 @@ where F: FnOnce(()) 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) { @@ -55,6 +104,15 @@ where F: FnOnce(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 { @@ -76,14 +134,14 @@ where F: FnOnce(T) impl PhantomDrop> { - /// Box the closure in this instance. + /// 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. + /// 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) }; @@ -91,7 +149,7 @@ impl PhantomDrop> } impl PhantomDrop { - /// Replace the function to be ran on drop with a no-op in place. + /// 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 }; @@ -108,3 +166,61 @@ where F: FnOnce(T) 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!")); + } +}