Skip to content

Commit 68f08c5

Browse files
committed
Add core::mem::DropGuard
Fix CI for drop_guard fix CI fix all tidy lints fix tidy link add first batch of feedback from review Add second batch of feedback from review add third batch of feedback from review fix failing test Update library/core/src/mem/drop_guard.rs Co-authored-by: Ruby Lazuli <[email protected]> fix doctests Implement changes from T-Libs-API review And start tracking based on the tracking issue. fix tidy lint
1 parent 9982d64 commit 68f08c5

File tree

5 files changed

+207
-0
lines changed

5 files changed

+207
-0
lines changed

library/core/src/mem/drop_guard.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use crate::fmt::{self, Debug};
2+
use crate::mem::ManuallyDrop;
3+
use crate::ops::{Deref, DerefMut};
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+
/// #![feature(drop_guard)]
14+
///
15+
/// use std::mem::DropGuard;
16+
///
17+
/// {
18+
/// // Create a new guard around a string that will
19+
/// // print its value when dropped.
20+
/// let s = String::from("Chashu likes tuna");
21+
/// let mut s = DropGuard::new(s, |s| println!("{s}"));
22+
///
23+
/// // Modify the string contained in the guard.
24+
/// s.push_str("!!!");
25+
///
26+
/// // The guard will be dropped here, printing:
27+
/// // "Chashu likes tuna!!!"
28+
/// }
29+
/// ```
30+
#[unstable(feature = "drop_guard", issue = "144426")]
31+
#[doc(alias = "ScopeGuard")]
32+
#[doc(alias = "defer")]
33+
pub struct DropGuard<T, F>
34+
where
35+
F: FnOnce(T),
36+
{
37+
inner: ManuallyDrop<T>,
38+
f: ManuallyDrop<F>,
39+
}
40+
41+
impl<T, F> DropGuard<T, F>
42+
where
43+
F: FnOnce(T),
44+
{
45+
/// Create a new instance of `DropGuard`.
46+
///
47+
/// # Example
48+
///
49+
/// ```rust
50+
/// # #![allow(unused)]
51+
/// #![feature(drop_guard)]
52+
///
53+
/// use std::mem::DropGuard;
54+
///
55+
/// let value = String::from("Chashu likes tuna");
56+
/// let guard = DropGuard::new(value, |s| println!("{s}"));
57+
/// ```
58+
#[unstable(feature = "drop_guard", issue = "144426")]
59+
#[must_use]
60+
pub const fn new(inner: T, f: F) -> Self {
61+
Self { inner: ManuallyDrop::new(inner), f: ManuallyDrop::new(f) }
62+
}
63+
64+
/// Consumes the `DropGuard`, returning the wrapped value.
65+
///
66+
/// This will not execute the closure. This is implemented as an associated
67+
/// function to prevent any potential conflicts with any other methods called
68+
/// `into_inner` from the `Deref` and `DerefMut` impls.
69+
///
70+
/// It is typically preferred to call this function instead of `mem::forget`
71+
/// because it will return the stored value and drop variables captured
72+
/// by the closure instead of leaking their owned resources.
73+
///
74+
/// # Example
75+
///
76+
/// ```rust
77+
/// # #![allow(unused)]
78+
/// #![feature(drop_guard)]
79+
///
80+
/// use std::mem::DropGuard;
81+
///
82+
/// let value = String::from("Nori likes chicken");
83+
/// let guard = DropGuard::new(value, |s| println!("{s}"));
84+
/// assert_eq!(DropGuard::into_inner(guard), "Nori likes chicken");
85+
/// ```
86+
#[unstable(feature = "drop_guard", issue = "144426")]
87+
#[inline]
88+
pub fn into_inner(guard: Self) -> T {
89+
// First we ensure that dropping the guard will not trigger
90+
// its destructor
91+
let mut guard = ManuallyDrop::new(guard);
92+
93+
// Next we manually read the stored value from the guard.
94+
//
95+
// SAFETY: this is safe because we've taken ownership of the guard.
96+
let value = unsafe { ManuallyDrop::take(&mut guard.inner) };
97+
98+
// Finally we drop the stored closure. We do this *after* having read
99+
// the value, so that even if the closure's `drop` function panics,
100+
// unwinding still tries to drop the value.
101+
//
102+
// SAFETY: this is safe because we've taken ownership of the guard.
103+
unsafe { ManuallyDrop::drop(&mut guard.f) };
104+
value
105+
}
106+
}
107+
108+
#[unstable(feature = "drop_guard", issue = "144426")]
109+
impl<T, F> Deref for DropGuard<T, F>
110+
where
111+
F: FnOnce(T),
112+
{
113+
type Target = T;
114+
115+
fn deref(&self) -> &T {
116+
&*self.inner
117+
}
118+
}
119+
120+
#[unstable(feature = "drop_guard", issue = "144426")]
121+
impl<T, F> DerefMut for DropGuard<T, F>
122+
where
123+
F: FnOnce(T),
124+
{
125+
fn deref_mut(&mut self) -> &mut T {
126+
&mut *self.inner
127+
}
128+
}
129+
130+
#[unstable(feature = "drop_guard", issue = "144426")]
131+
impl<T, F> Drop for DropGuard<T, F>
132+
where
133+
F: FnOnce(T),
134+
{
135+
fn drop(&mut self) {
136+
// SAFETY: `DropGuard` is in the process of being dropped.
137+
let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
138+
139+
// SAFETY: `DropGuard` is in the process of being dropped.
140+
let f = unsafe { ManuallyDrop::take(&mut self.f) };
141+
142+
f(inner);
143+
}
144+
}
145+
146+
#[unstable(feature = "drop_guard", issue = "144426")]
147+
impl<T, F> Debug for DropGuard<T, F>
148+
where
149+
T: Debug,
150+
F: FnOnce(T),
151+
{
152+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153+
fmt::Debug::fmt(&**self, f)
154+
}
155+
}

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 = "144426")]
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/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#![feature(core_private_diy_float)]
3131
#![feature(cstr_display)]
3232
#![feature(dec2flt)]
33+
#![feature(drop_guard)]
3334
#![feature(duration_constants)]
3435
#![feature(duration_constructors)]
3536
#![feature(duration_constructors_lite)]

library/coretests/tests/mem.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use core::mem::*;
22
use core::{array, ptr};
3+
use std::cell::Cell;
34
#[cfg(panic = "unwind")]
45
use std::rc::Rc;
56

@@ -795,3 +796,48 @@ fn const_maybe_uninit_zeroed() {
795796

796797
assert_eq!(unsafe { (*UNINIT.0.cast::<[[u8; SIZE]; 1]>())[0] }, [0u8; SIZE]);
797798
}
799+
800+
#[test]
801+
fn drop_guards_only_dropped_by_closure_when_run() {
802+
let value_drops = Cell::new(0);
803+
let value = DropGuard::new((), |()| value_drops.set(1 + value_drops.get()));
804+
let closure_drops = Cell::new(0);
805+
let guard = DropGuard::new(value, |_| closure_drops.set(1 + closure_drops.get()));
806+
assert_eq!(value_drops.get(), 0);
807+
assert_eq!(closure_drops.get(), 0);
808+
drop(guard);
809+
assert_eq!(value_drops.get(), 1);
810+
assert_eq!(closure_drops.get(), 1);
811+
}
812+
813+
#[test]
814+
fn drop_guard_into_inner() {
815+
let dropped = Cell::new(false);
816+
let value = DropGuard::new(42, |_| dropped.set(true));
817+
let guard = DropGuard::new(value, |_| dropped.set(true));
818+
let inner = DropGuard::into_inner(guard);
819+
assert_eq!(dropped.get(), false);
820+
assert_eq!(*inner, 42);
821+
}
822+
823+
#[test]
824+
#[cfg(panic = "unwind")]
825+
fn drop_guard_always_drops_value_if_closure_drop_unwinds() {
826+
// Create a value with a destructor, which we will validate ran successfully.
827+
let mut value_was_dropped = false;
828+
let value_with_tracked_destruction = DropGuard::new((), |_| value_was_dropped = true);
829+
830+
// Create a closure that will begin unwinding when dropped.
831+
let drop_bomb = DropGuard::new((), |_| panic!());
832+
let closure_that_panics_on_drop = move |_| {
833+
let _drop_bomb = drop_bomb;
834+
};
835+
836+
// This will run the closure, which will panic when dropped. This should
837+
// run the destructor of the value we passed, which we validate.
838+
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
839+
let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop);
840+
DropGuard::into_inner(guard);
841+
}));
842+
assert!(value_was_dropped);
843+
}

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)