From ab44df311b4121fe6c5a4e0775bea60925a4d5b5 Mon Sep 17 00:00:00 2001 From: Michael Pfaff Date: Sat, 3 Feb 2024 17:38:14 -0500 Subject: [PATCH] Add `defer_with_data_by` helper function --- src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index be5cab1..f50d2b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,11 +15,31 @@ //! //! 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}; @@ -33,6 +53,14 @@ 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; @@ -101,3 +129,13 @@ impl DerefMut for DropGuard { &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) + } +}