diff --git a/library/core/src/mem/drop_guard.rs b/library/core/src/mem/drop_guard.rs index 74bf353907455..e05804176a4f5 100644 --- a/library/core/src/mem/drop_guard.rs +++ b/library/core/src/mem/drop_guard.rs @@ -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("!!!"); @@ -31,7 +52,7 @@ use crate::ops::{Deref, DerefMut}; #[unstable(feature = "drop_guard", issue = "144426")] #[doc(alias = "ScopeGuard")] #[doc(alias = "defer")] -pub struct DropGuard +pub struct DropGuard> where F: FnOnce(T), { @@ -39,11 +60,54 @@ where f: ManuallyDrop, } +/// 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 DropGuard<(), UnitFn> +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 DropGuard where F: FnOnce(T), { - /// Create a new instance of `DropGuard`. + /// Create a new instance of `DropGuard` with a value and a cleanup closure. /// /// # Example /// @@ -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) } } @@ -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")] @@ -158,3 +222,28 @@ where fmt::Debug::fmt(&**self, f) } } + +#[unstable(feature = "drop_guard", issue = "144426")] +pub struct UnitFn(F); + +#[unstable(feature = "drop_guard", issue = "144426")] +impl FnOnce<((),)> for UnitFn +where + F: FnOnce(), +{ + type Output = (); + + extern "rust-call" fn call_once(self, _args: ((),)) -> Self::Output { + (self.0)() + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl Debug for UnitFn +where + F: FnOnce(), +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnitFn") + } +} diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index ad5fda0cfe4db..1fd9d9e8ec37e 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -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. diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index 5247e01fba01b..3786ab0365902 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -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); @@ -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); @@ -825,10 +825,10 @@ 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; }; @@ -836,8 +836,17 @@ fn drop_guard_always_drops_value_if_closure_drop_unwinds() { // 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(); + }; +} diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index 18cdea0e1b1b3..fb018c70f111d 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -48,7 +48,7 @@ impl Thread { let data = init; let mut attr: mem::MaybeUninit = 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) });