Skip to content

Commit bb82763

Browse files
committed
Add core::mem::DropGuard
1 parent 9982d64 commit bb82763

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

library/core/src/mem/drop_guard.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
use core::mem::ManuallyDrop;
2+
use core::ops::{Deref, DerefMut};
3+
use core::ptr;
4+
5+
/// Wrap a value and run a closure when dropped.
6+
///
7+
/// This is useful for quickly creating desructors inline.
8+
///
9+
/// # Examples
10+
///
11+
/// ```rust
12+
/// # #![allow(unused)]
13+
/// use std::mem::DropGuard;
14+
///
15+
/// {
16+
/// // Create a new guard around a string that will
17+
/// // print its value when dropped.
18+
/// let s = String::from("Chashu likes tuna");
19+
/// let mut s = DropGuard::new(s, |s| println!("{s}"));
20+
///
21+
/// // Modify the string contained in the guard.
22+
/// s.push_str("!!!");
23+
///
24+
/// // The guard will be dropped here, printing:
25+
/// // "Chashu likes tuna!!!"
26+
/// }
27+
/// ```
28+
#[unstable(feature = "drop_guard", issue = "none")]
29+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
30+
pub struct DropGuard<T, F>
31+
where
32+
F: FnOnce(T),
33+
{
34+
inner: ManuallyDrop<T>,
35+
f: ManuallyDrop<F>,
36+
}
37+
38+
impl<T, F> DropGuard<T, F>
39+
where
40+
F: FnOnce(T),
41+
{
42+
/// Create a new instance of `DropGuard`.
43+
///
44+
/// # Example
45+
///
46+
/// ```rust
47+
/// # #![allow(unused)]
48+
/// use std::mem::DropGuard;
49+
///
50+
/// let value = String::from("Chashu likes tuna");
51+
/// let guard = DropGuard::new(value, |s| println!("{s}"));
52+
/// ```
53+
#[unstable(feature = "drop_guard", issue = "none")]
54+
pub fn new(inner: T, f: F) -> Self {
55+
Self {
56+
inner: ManuallyDrop::new(inner),
57+
f: ManuallyDrop::new(f),
58+
}
59+
}
60+
61+
/// Consumes the `Guard`, returning the wrapped value.
62+
///
63+
/// This will not execute the closure. This is implemented as a static
64+
/// method to prevent any potential conflicts with any other methods called
65+
/// `into_inner` from the `Deref` and `DerefMut` impls.
66+
///
67+
/// It is typically preferred to call this function instead of `mem::forget`
68+
/// because it will appropriately de-initialize the closure stored in the
69+
/// `DropGuard` before returning the stored value.
70+
///
71+
/// # Example
72+
///
73+
/// ```rust
74+
/// # #![allow(unused)]
75+
/// use std::mem::DropGuard;
76+
///
77+
/// let value = String::from("Nori likes chicken");
78+
/// let guard = DropGuard::new(value, |s| println!("{s}"));
79+
/// assert_eq!(Guard::into_inner(guard), "Nori likes chicken");
80+
/// ```
81+
#[unstable(feature = "drop_guard", issue = "none")]
82+
#[inline]
83+
pub fn into_inner(guard: Self) -> T {
84+
// Implementation note: this is the same approach that scopeguard takes.
85+
86+
// First we ensure that dropping the guard will not trigger
87+
// its destructor
88+
let mut guard = ManuallyDrop::new(guard);
89+
90+
// Next we manually read the stored value from the guard.
91+
//
92+
// SAFETY: this is safe because we've taken ownership of the guard.
93+
let value = unsafe { ptr::read(&*guard.inner) };
94+
95+
// Finally we drop the stored closure. We do this *after* having read
96+
// the value, so that even if the closure's `drop` function panics,
97+
// unwinding still tries to drop the value.
98+
//
99+
// SAFETY: this is safe because we've taken ownership of the guard.
100+
unsafe { ManuallyDrop::drop(&mut guard.f) };
101+
value
102+
}
103+
}
104+
105+
#[unstable(feature = "drop_guard", issue = "none")]
106+
impl<T, F> Deref for DropGuard<T, F>
107+
where
108+
F: FnOnce(T),
109+
{
110+
type Target = T;
111+
112+
fn deref(&self) -> &T {
113+
&*self.inner
114+
}
115+
}
116+
117+
#[unstable(feature = "drop_guard", issue = "none")]
118+
impl<T, F> DerefMut for DropGuard<T, F>
119+
where
120+
F: FnOnce(T),
121+
{
122+
fn deref_mut(&mut self) -> &mut T {
123+
&mut *self.inner
124+
}
125+
}
126+
127+
#[unstable(feature = "drop_guard", issue = "none")]
128+
impl<T, F> Drop for DropGuard<T, F>
129+
where
130+
F: FnOnce(T),
131+
{
132+
fn drop(&mut self) {
133+
// SAFETY: we're taking the values out of the `ManuallyDrop` with
134+
// the express intent of dropping them.
135+
let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
136+
let f = unsafe { ManuallyDrop::take(&mut self.f) };
137+
f(inner);
138+
}
139+
}
140+
141+
// tests copied from https://docs.rs/scopeguard/latest/src/scopeguard/lib.rs.html#1-595
142+
#[cfg(test)]
143+
mod tests {
144+
use super::*;
145+
use std::cell::Cell;
146+
147+
#[test]
148+
fn test_only_dropped_by_closure_when_run() {
149+
let value_drops = Cell::new(0);
150+
let value = DropGuard::new((), |()| value_drops.set(1 + value_drops.get()));
151+
let closure_drops = Cell::new(0);
152+
let guard = DropGuard::new(value, |_| closure_drops.set(1 + closure_drops.get()));
153+
assert_eq!(value_drops.get(), 0);
154+
assert_eq!(closure_drops.get(), 0);
155+
drop(guard);
156+
assert_eq!(value_drops.get(), 1);
157+
assert_eq!(closure_drops.get(), 1);
158+
}
159+
160+
#[test]
161+
fn test_into_inner() {
162+
let dropped = Cell::new(false);
163+
let value = DropGuard::new(42, |_| dropped.set(true));
164+
let guard = DropGuard::new(value, |_| dropped.set(true));
165+
let inner = DropGuard::into_inner(guard);
166+
assert_eq!(dropped.get(), false);
167+
assert_eq!(*inner, 42);
168+
}
169+
}

library/core/src/mem/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ mod transmutability;
2121
#[unstable(feature = "transmutability", issue = "99571")]
2222
pub use transmutability::{Assume, TransmuteFrom};
2323

24+
mod drop_guard;
25+
#[unstable(feature = "drop_guard", issue = "none")]
26+
pub use drop_guard::DropGuard;
27+
2428
// This one has to be a re-export (rather than wrapping the underlying intrinsic) so that we can do
2529
// the special magic "types have equal size" check at the call site.
2630
#[stable(feature = "rust1", since = "1.0.0")]

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@
331331
#![feature(clone_to_uninit)]
332332
#![feature(core_intrinsics)]
333333
#![feature(core_io_borrowed_buf)]
334+
#![feature(drop_guard)]
334335
#![feature(duration_constants)]
335336
#![feature(error_generic_member_access)]
336337
#![feature(error_iter)]

0 commit comments

Comments
 (0)