//! Generic RAII wrappers that execute actions when the guard is dropped in the style of Zig's //! [`defer`](https://ziglang.org/documentation/master/#defer) statement. //! //! Take this rather contrived example: //! ``` //! use std::cell::RefCell; //! use drop_guard::defer; //! //! let s = RefCell::new(String::new()); //! { //! let _guard = defer(|| s.borrow_mut().push_str("World")); //! let _guard = defer(|| s.borrow_mut().push_str(", ")); //! s.borrow_mut().push_str("Hello"); //! } //! //! assert_eq!(s.into_inner(), "Hello, World"); //! ``` //! //! Of course, it sucks to need to resort to runtime borrow checking. Thankfully, the example can be rewritten like so: //! ``` //! use drop_guard::defer_with_data_by; //! //! let mut s = String::new(); //! { //! let mut s = defer_with_data_by(|s| s.push_str("World"), &mut s); //! let mut s = defer_with_data_by(|s| s.push_str(", "), &mut s); //! s.push_str("Hello"); //! } //! //! assert_eq!(s, "Hello, World"); //! ``` //! //! But, what's going on here? Well, [`DropGuard`] holds on to two values: a [`DropFn`], and an //! optional `data`. When we just call `defer`, `data` will be a `()`, but when we call //! `defer_with_data_by` instead, it will be the second argument. [`DropGuard`] derefences to that data value, enabling you to keep using that //! data while it's guarded! pub(crate) mod macros; pub mod ext; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; /// Defers execution of the provided [`DropFn`] until the returned [`DropGuard`] is dropped. pub const fn defer>(f: F) -> DropGuard { DropGuard::new(f) } /// Defers execution of the provided [`DropFn`], with data `d`, until the returned [`DropGuard`] is dropped. pub const fn defer_with_data(f: F, d: F::Data) -> DropGuard { DropGuard::with_data(f, d) } /// Defers execution of the provided [`DropFn`], with data `d`, until the returned [`DropGuard`] is dropped. pub const fn defer_with_data_by(f: F, d: D) -> DropGuard> where F: FnOnce(D) -> (), { defer_with_data(FnOnceDropFn(f, PhantomData), d) } pub trait DropFn { // TODO: Use a default of `()` once the associated_type_defaults (rust-lang/rust#29661) feature lands type Data; fn on_drop(self, data: Self::Data); } impl ()> DropFn for F { type Data = (); fn on_drop(self, _: ()) { self() } } pub struct DropGuard(ManuallyDrop, ManuallyDrop); impl DropGuard { /// Same as [`defer_with_data`]. pub const fn with_data(f: F, d: F::Data) -> Self { Self(ManuallyDrop::new(f), ManuallyDrop::new(d)) } pub fn unguard(slot: Self) -> F::Data { Self::into_inner(slot).1 } pub fn into_inner(slot: Self) -> (F, F::Data) { let mut slot = ManuallyDrop::new(slot); // SAFETY: the DropGuard (slot) is wrapped in a ManuallyDrop, so its Drop impl will not be run unsafe { ( ManuallyDrop::take(&mut slot.0), ManuallyDrop::take(&mut slot.1), ) } } } impl> DropGuard { /// Same as [`defer`]. pub const fn new(f: F) -> Self { Self::with_data(f, ()) } } impl Drop for DropGuard { fn drop(&mut self) { // SAFETY: see guarantees on the Drop trait let f = unsafe { ManuallyDrop::take(&mut self.0) }; let d = unsafe { ManuallyDrop::take(&mut self.1) }; f.on_drop(d); } } impl Deref for DropGuard { type Target = F::Data; fn deref(&self) -> &Self::Target { &self.1 } } impl DerefMut for DropGuard { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.1 } } pub struct FnOnceDropFn (), D>(F, PhantomData ()>); impl (), D> DropFn for FnOnceDropFn { type Data = D; fn on_drop(self, data: Self::Data) { self.0(data) } }