mini_scopeguard/lib.rs
1//! A minimal scopeguard implementation
2//!
3//! # Examples
4//!
5//! ```rust
6//! # #![allow(unused)]
7//! # use mini_scopeguard::Guard;
8//! {
9//! // Create a new guard around a string that will
10//! // print its value when dropped.
11//! let s = String::from("Chashu likes tuna");
12//! let mut s = Guard::new(s, |s| println!("{s}"));
13//!
14//! // Modify the string contained in the guard.
15//! s.push_str("!!!");
16//!
17//! // The guard will be dropped here.
18//! }
19//! ```
20//!
21//! # Comparison to `scopeguard`
22//!
23//! The scopeguard crate provides several settings to configure when exactly the
24//! closure should run. This seems like a fairly niche capability, and so this
25//! crate removes those. The implementation of this crate borrows key parts from
26//! `scopeguard`, and credit goes out to its authors for figuring out the hard parts.
27
28#![deny(missing_debug_implementations, nonstandard_style)]
29#![warn(missing_docs, future_incompatible, unreachable_pub)]
30
31use core::mem::ManuallyDrop;
32use core::ops::{Deref, DerefMut};
33use core::ptr;
34
35/// Wrap a value and run a closure when dropped.
36///
37/// This is useful for quickly creating desructors inline.
38///
39/// # Examples
40///
41/// ```rust
42/// # #![allow(unused)]
43/// # use mini_scopeguard::Guard;
44/// {
45/// // Create a new guard around a string that will
46/// // print its value when dropped.
47/// let s = String::from("Chashu likes tuna");
48/// let mut s = Guard::new(s, |s| println!("{s}"));
49///
50/// // Modify the string contained in the guard.
51/// s.push_str("!!!");
52///
53/// // The guard will be dropped here.
54/// }
55/// ```
56#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
57pub struct Guard<T, F>
58where
59 F: FnOnce(T),
60{
61 inner: ManuallyDrop<T>,
62 f: ManuallyDrop<F>,
63}
64
65impl<T, F> Guard<T, F>
66where
67 F: FnOnce(T),
68{
69 /// Create a new instance of `Guard`.
70 ///
71 /// # Example
72 ///
73 /// ```rust
74 /// # #![allow(unused)]
75 /// # use mini_scopeguard::Guard;
76 /// let value = String::from("Chashu likes tuna");
77 /// let guard = Guard::new(value, |s| println!("{s}"));
78 /// ```
79 pub fn new(inner: T, f: F) -> Self {
80 Self {
81 inner: ManuallyDrop::new(inner),
82 f: ManuallyDrop::new(f),
83 }
84 }
85
86 /// Consumes the `Guard`, returning the wrapped value.
87 ///
88 /// This will not execute the closure. This is implemented as a static
89 /// method to prevent any potential conflicts with any other methods called
90 /// `into_inner` inherited via the `Deref` and `DerefMut` impls.
91 ///
92 /// # Example
93 ///
94 /// ```rust
95 /// # #![allow(unused)]
96 /// # use mini_scopeguard::Guard;
97 /// let value = String::from("Nori likes chicken");
98 /// let guard = Guard::new(value, |s| println!("{s}"));
99 /// assert_eq!(Guard::into_inner(guard), "Nori likes chicken");
100 /// ```
101 // Copied the impl from: https://docs.rs/scopeguard/latest/src/scopeguard/lib.rs.html#304-313
102 #[inline]
103 pub fn into_inner(guard: Self) -> T {
104 // Cannot move out of `Drop`-implementing types,
105 // so `ptr::read` the value and forget the guard.
106 let mut guard = ManuallyDrop::new(guard);
107 unsafe {
108 let value = ptr::read(&*guard.inner);
109 // Drop the closure after `value` has been read, so that if the
110 // closure's `drop` function panics, unwinding still tries to drop
111 // `value`.
112 ManuallyDrop::drop(&mut guard.f);
113 value
114 }
115 }
116}
117impl<T, F> Deref for Guard<T, F>
118where
119 F: FnOnce(T),
120{
121 type Target = T;
122
123 fn deref(&self) -> &T {
124 &*self.inner
125 }
126}
127
128impl<T, F> DerefMut for Guard<T, F>
129where
130 F: FnOnce(T),
131{
132 fn deref_mut(&mut self) -> &mut T {
133 &mut *self.inner
134 }
135}
136
137impl<T, F> Drop for Guard<T, F>
138where
139 F: FnOnce(T),
140{
141 fn drop(&mut self) {
142 // SAFETY: we're taking the values out of the `ManuallyDrop` with
143 // the express intent to drop them.
144 let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
145 let f = unsafe { ManuallyDrop::take(&mut self.f) };
146 f(inner);
147 }
148}
149
150// tests copied from https://docs.rs/scopeguard/latest/src/scopeguard/lib.rs.html#1-595
151#[cfg(test)]
152mod tests {
153 use super::*;
154 use std::cell::Cell;
155
156 #[test]
157 fn test_only_dropped_by_closure_when_run() {
158 let value_drops = Cell::new(0);
159 let value = Guard::new((), |()| value_drops.set(1 + value_drops.get()));
160 let closure_drops = Cell::new(0);
161 let guard = Guard::new(value, |_| closure_drops.set(1 + closure_drops.get()));
162 assert_eq!(value_drops.get(), 0);
163 assert_eq!(closure_drops.get(), 0);
164 drop(guard);
165 assert_eq!(value_drops.get(), 1);
166 assert_eq!(closure_drops.get(), 1);
167 }
168
169 #[test]
170 fn test_into_inner() {
171 let dropped = Cell::new(false);
172 let value = Guard::new(42, |_| dropped.set(true));
173 let guard = Guard::new(value, |_| dropped.set(true));
174 let inner = Guard::into_inner(guard);
175 assert_eq!(dropped.get(), false);
176 assert_eq!(*inner, 42);
177 }
178}