mini-scopeguard 1.0.0

A minimal scopeguard implementation
Documentation
//! A minimal scopeguard implementation
//!
//! # Examples
//!
//! ```rust
//! # #![allow(unused)]
//! # use mini_scopeguard::Guard;
//! {
//!     // Create a new guard around a string that will
//!     // print its value when dropped.
//!     let s = String::from("Chashu likes tuna");
//!     let mut s = Guard::new(s, |s| println!("{s}"));
//!     
//!     // Modify the string contained in the guard.
//!     s.push_str("!!!");
//!     
//!     // The guard will be dropped here.
//! }
//! ```
//!
//! # Comparison to `scopeguard`
//!
//! The scopeguard crate provides several settings to configure when exactly the
//! closure should run. This seems like a fairly niche capability, and so this
//! crate removes those. The implementation of this crate borrows key parts from
//! `scopeguard`, and credit goes out to its authors for figuring out the hard parts.

#![deny(missing_debug_implementations, nonstandard_style)]
#![warn(missing_docs, future_incompatible, unreachable_pub)]

use core::mem::ManuallyDrop;
use core::ops::{Deref, DerefMut};
use core::ptr;

/// Wrap a value and run a closure when dropped.
///
/// This is useful for quickly creating desructors inline.
///
/// # Examples
///
/// ```rust
/// # #![allow(unused)]
/// # use mini_scopeguard::Guard;
/// {
///     // Create a new guard around a string that will
///     // print its value when dropped.
///     let s = String::from("Chashu likes tuna");
///     let mut s = Guard::new(s, |s| println!("{s}"));
///     
///     // Modify the string contained in the guard.
///     s.push_str("!!!");
///     
///     // The guard will be dropped here.
/// }
/// ```
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Guard<T, F>
where
    F: FnOnce(T),
{
    inner: ManuallyDrop<T>,
    f: ManuallyDrop<F>,
}

impl<T, F> Guard<T, F>
where
    F: FnOnce(T),
{
    /// Create a new instance of `Guard`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #![allow(unused)]
    /// # use mini_scopeguard::Guard;
    /// let value = String::from("Chashu likes tuna");
    /// let guard = Guard::new(value, |s| println!("{s}"));
    /// ```
    pub fn new(inner: T, f: F) -> Self {
        Self {
            inner: ManuallyDrop::new(inner),
            f: ManuallyDrop::new(f),
        }
    }

    /// Consumes the `Guard`, returning the wrapped value.
    ///
    /// This will not execute the closure. This is implemented as a static
    /// method to prevent any potential conflicts with any other methods called
    /// `into_inner` inherited via the `Deref` and `DerefMut` impls.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #![allow(unused)]
    /// # use mini_scopeguard::Guard;
    /// let value = String::from("Nori likes chicken");
    /// let guard = Guard::new(value, |s| println!("{s}"));
    /// assert_eq!(Guard::into_inner(guard), "Nori likes chicken");
    /// ```
    // Copied the impl from: https://docs.rs/scopeguard/latest/src/scopeguard/lib.rs.html#304-313
    #[inline]
    pub fn into_inner(guard: Self) -> T {
        // Cannot move out of `Drop`-implementing types,
        // so `ptr::read` the value and forget the guard.
        let mut guard = ManuallyDrop::new(guard);
        unsafe {
            let value = ptr::read(&*guard.inner);
            // Drop the closure after `value` has been read, so that if the
            // closure's `drop` function panics, unwinding still tries to drop
            // `value`.
            ManuallyDrop::drop(&mut guard.f);
            value
        }
    }
}
impl<T, F> Deref for Guard<T, F>
where
    F: FnOnce(T),
{
    type Target = T;

    fn deref(&self) -> &T {
        &*self.inner
    }
}

impl<T, F> DerefMut for Guard<T, F>
where
    F: FnOnce(T),
{
    fn deref_mut(&mut self) -> &mut T {
        &mut *self.inner
    }
}

impl<T, F> Drop for Guard<T, F>
where
    F: FnOnce(T),
{
    fn drop(&mut self) {
        // SAFETY: we're taking the values out of the `ManuallyDrop` with
        // the express intent to drop them.
        let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
        let f = unsafe { ManuallyDrop::take(&mut self.f) };
        f(inner);
    }
}

// tests copied from https://docs.rs/scopeguard/latest/src/scopeguard/lib.rs.html#1-595
#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::Cell;

    #[test]
    fn test_only_dropped_by_closure_when_run() {
        let value_drops = Cell::new(0);
        let value = Guard::new((), |()| value_drops.set(1 + value_drops.get()));
        let closure_drops = Cell::new(0);
        let guard = Guard::new(value, |_| closure_drops.set(1 + closure_drops.get()));
        assert_eq!(value_drops.get(), 0);
        assert_eq!(closure_drops.get(), 0);
        drop(guard);
        assert_eq!(value_drops.get(), 1);
        assert_eq!(closure_drops.get(), 1);
    }

    #[test]
    fn test_into_inner() {
        let dropped = Cell::new(false);
        let value = Guard::new(42, |_| dropped.set(true));
        let guard = Guard::new(value, |_| dropped.set(true));
        let inner = Guard::into_inner(guard);
        assert_eq!(dropped.get(), false);
        assert_eq!(*inner, 42);
    }
}