Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 95 additions & 6 deletions library/core/src/mem/drop_guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,33 @@ use crate::ops::{Deref, DerefMut};
/// #![feature(drop_guard)]
///
/// use std::mem::DropGuard;
/// use std::mem::defer;
///
/// {
/// // Create a new guard that will do something
/// // when dropped.
/// defer! {
/// println!("Goodbye, world!");
/// }
///
/// // The guard will be dropped here, printing:
/// // "Goodbye, world!"
/// }
///
/// {
/// // Create a new guard that will do something
/// // when dropped.
/// let _guard = DropGuard::new(|| println!("Goodbye, world!"));
///
/// // The guard will be dropped here, printing:
/// // "Goodbye, world!"
/// }
///
/// {
/// // Create a new guard around a string that will
/// // print its value when dropped.
/// let s = String::from("Chashu likes tuna");
/// let mut s = DropGuard::new(s, |s| println!("{s}"));
/// let mut s = DropGuard::with_value(s, |s| println!("{s}"));
///
/// // Modify the string contained in the guard.
/// s.push_str("!!!");
Expand All @@ -31,19 +52,62 @@ use crate::ops::{Deref, DerefMut};
#[unstable(feature = "drop_guard", issue = "144426")]
#[doc(alias = "ScopeGuard")]
#[doc(alias = "defer")]
pub struct DropGuard<T, F>
pub struct DropGuard<T = (), F = UnitFn<fn()>>
where
F: FnOnce(T),
{
inner: ManuallyDrop<T>,
f: ManuallyDrop<F>,
}

/// Create an anonymous `DropGuard` with a cleanup closure.
///
/// The macro takes statements, which are the body of a closure
/// that will run when the scope is exited.
///
/// # Example
///
/// ```rust
/// # #![allow(unused)]
/// #![feature(drop_guard)]
///
/// use std::mem::defer;
///
/// defer! { println!("Goodbye, world!") };
/// ```
#[unstable(feature = "drop_guard", issue = "144426")]
pub macro defer($($t:tt)*) {
let _guard = $crate::mem::DropGuard::new(|| { $($t)* });
}

impl<F> DropGuard<(), UnitFn<F>>
where
F: FnOnce(),
{
/// Create a new instance of `DropGuard` with a cleanup closure.
///
/// # Example
///
/// ```rust
/// # #![allow(unused)]
/// #![feature(drop_guard)]
///
/// use std::mem::DropGuard;
///
/// let guard = DropGuard::new(|| println!("Goodbye, world!"));
/// ```
#[unstable(feature = "drop_guard", issue = "144426")]
#[must_use]
pub const fn new(f: F) -> Self {
Self { inner: ManuallyDrop::new(()), f: ManuallyDrop::new(UnitFn(f)) }
}
}

impl<T, F> DropGuard<T, F>
where
F: FnOnce(T),
{
/// Create a new instance of `DropGuard`.
/// Create a new instance of `DropGuard` with a value and a cleanup closure.
///
/// # Example
///
Expand All @@ -54,11 +118,11 @@ where
/// use std::mem::DropGuard;
///
/// let value = String::from("Chashu likes tuna");
/// let guard = DropGuard::new(value, |s| println!("{s}"));
/// let guard = DropGuard::with_value(value, |s| println!("{s}"));
/// ```
#[unstable(feature = "drop_guard", issue = "144426")]
#[must_use]
pub const fn new(inner: T, f: F) -> Self {
pub const fn with_value(inner: T, f: F) -> Self {
Self { inner: ManuallyDrop::new(inner), f: ManuallyDrop::new(f) }
}

Expand All @@ -78,7 +142,7 @@ where
/// use std::mem::DropGuard;
///
/// let value = String::from("Nori likes chicken");
/// let guard = DropGuard::new(value, |s| println!("{s}"));
/// let guard = DropGuard::with_value(value, |s| println!("{s}"));
/// assert_eq!(DropGuard::dismiss(guard), "Nori likes chicken");
/// ```
#[unstable(feature = "drop_guard", issue = "144426")]
Expand Down Expand Up @@ -158,3 +222,28 @@ where
fmt::Debug::fmt(&**self, f)
}
}

#[unstable(feature = "drop_guard", issue = "144426")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be permanently unstable. But we don't export it anyway.

pub struct UnitFn<F>(F);

#[unstable(feature = "drop_guard", issue = "144426")]
impl<F> FnOnce<((),)> for UnitFn<F>
where
F: FnOnce(),
{
type Output = ();

extern "rust-call" fn call_once(self, _args: ((),)) -> Self::Output {
(self.0)()
}
}

#[unstable(feature = "drop_guard", issue = "144426")]
impl<F> Debug for UnitFn<F>
where
F: FnOnce(),
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UnitFn")
}
}
2 changes: 1 addition & 1 deletion library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub use transmutability::{Assume, TransmuteFrom};

mod drop_guard;
#[unstable(feature = "drop_guard", issue = "144426")]
pub use drop_guard::DropGuard;
pub use drop_guard::{DropGuard, defer};

// This one has to be a re-export (rather than wrapping the underlying intrinsic) so that we can do
// the special magic "types have equal size" check at the call site.
Expand Down
23 changes: 16 additions & 7 deletions library/coretests/tests/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -800,9 +800,9 @@ fn const_maybe_uninit_zeroed() {
#[test]
fn drop_guards_only_dropped_by_closure_when_run() {
let value_drops = Cell::new(0);
let value = DropGuard::new((), |()| value_drops.set(1 + value_drops.get()));
let value = DropGuard::new(|| value_drops.set(1 + value_drops.get()));
let closure_drops = Cell::new(0);
let guard = DropGuard::new(value, |_| closure_drops.set(1 + closure_drops.get()));
let guard = DropGuard::with_value(value, |_| closure_drops.set(1 + closure_drops.get()));
assert_eq!(value_drops.get(), 0);
assert_eq!(closure_drops.get(), 0);
drop(guard);
Expand All @@ -813,8 +813,8 @@ fn drop_guards_only_dropped_by_closure_when_run() {
#[test]
fn drop_guard_into_inner() {
let dropped = Cell::new(false);
let value = DropGuard::new(42, |_| dropped.set(true));
let guard = DropGuard::new(value, |_| dropped.set(true));
let value = DropGuard::with_value(42, |_| dropped.set(true));
let guard = DropGuard::with_value(value, |_| dropped.set(true));
let inner = DropGuard::dismiss(guard);
assert_eq!(dropped.get(), false);
assert_eq!(*inner, 42);
Expand All @@ -825,19 +825,28 @@ fn drop_guard_into_inner() {
fn drop_guard_always_drops_value_if_closure_drop_unwinds() {
// Create a value with a destructor, which we will validate ran successfully.
let mut value_was_dropped = false;
let value_with_tracked_destruction = DropGuard::new((), |_| value_was_dropped = true);
let value_with_tracked_destruction = DropGuard::new(|| value_was_dropped = true);

// Create a closure that will begin unwinding when dropped.
let drop_bomb = DropGuard::new((), |_| panic!());
let drop_bomb = DropGuard::new(|| panic!());
let closure_that_panics_on_drop = move |_| {
let _drop_bomb = drop_bomb;
};

// This will run the closure, which will panic when dropped. This should
// run the destructor of the value we passed, which we validate.
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop);
let guard =
DropGuard::with_value(value_with_tracked_destruction, closure_that_panics_on_drop);
DropGuard::dismiss(guard);
}));
assert!(value_was_dropped);
}

#[test]
fn defer_moved_value() {
let data = "owned data".to_string();
defer! {
std::thread::spawn(move || { assert_eq!(data.as_str(), "owned data") }).join().ok();
};
}
2 changes: 1 addition & 1 deletion library/std/src/sys/thread/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Thread {
let data = init;
let mut attr: mem::MaybeUninit<libc::pthread_attr_t> = mem::MaybeUninit::uninit();
assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0);
let mut attr = DropGuard::new(&mut attr, |attr| {
let mut attr = DropGuard::with_value(&mut attr, |attr| {
assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0)
});

Expand Down
Loading