about summary refs log tree commit diff
path: root/library
diff options
context:
space:
mode:
Diffstat (limited to 'library')
-rw-r--r--library/core/src/mem/drop_guard.rs155
-rw-r--r--library/core/src/mem/mod.rs4
-rw-r--r--library/core/src/ptr/mod.rs6
-rw-r--r--library/core/src/slice/mod.rs2
-rw-r--r--library/core/src/sync/atomic.rs18
-rw-r--r--library/coretests/tests/lib.rs1
-rw-r--r--library/coretests/tests/mem.rs46
-rw-r--r--library/std/src/lib.rs1
8 files changed, 221 insertions, 12 deletions
diff --git a/library/core/src/mem/drop_guard.rs b/library/core/src/mem/drop_guard.rs
new file mode 100644
index 00000000000..47ccb69acc8
--- /dev/null
+++ b/library/core/src/mem/drop_guard.rs
@@ -0,0 +1,155 @@
+use crate::fmt::{self, Debug};
+use crate::mem::ManuallyDrop;
+use crate::ops::{Deref, DerefMut};
+
+/// Wrap a value and run a closure when dropped.
+///
+/// This is useful for quickly creating desructors inline.
+///
+/// # Examples
+///
+/// ```rust
+/// # #![allow(unused)]
+/// #![feature(drop_guard)]
+///
+/// use std::mem::DropGuard;
+///
+/// {
+///     // 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}"));
+///
+///     // Modify the string contained in the guard.
+///     s.push_str("!!!");
+///
+///     // The guard will be dropped here, printing:
+///     // "Chashu likes tuna!!!"
+/// }
+/// ```
+#[unstable(feature = "drop_guard", issue = "144426")]
+#[doc(alias = "ScopeGuard")]
+#[doc(alias = "defer")]
+pub struct DropGuard<T, F>
+where
+    F: FnOnce(T),
+{
+    inner: ManuallyDrop<T>,
+    f: ManuallyDrop<F>,
+}
+
+impl<T, F> DropGuard<T, F>
+where
+    F: FnOnce(T),
+{
+    /// Create a new instance of `DropGuard`.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// #![feature(drop_guard)]
+    ///
+    /// use std::mem::DropGuard;
+    ///
+    /// let value = String::from("Chashu likes tuna");
+    /// let guard = DropGuard::new(value, |s| println!("{s}"));
+    /// ```
+    #[unstable(feature = "drop_guard", issue = "144426")]
+    #[must_use]
+    pub const fn new(inner: T, f: F) -> Self {
+        Self { inner: ManuallyDrop::new(inner), f: ManuallyDrop::new(f) }
+    }
+
+    /// Consumes the `DropGuard`, returning the wrapped value.
+    ///
+    /// This will not execute the closure. This is implemented as an associated
+    /// function to prevent any potential conflicts with any other methods called
+    /// `into_inner` from the `Deref` and `DerefMut` impls.
+    ///
+    /// It is typically preferred to call this function instead of `mem::forget`
+    /// because it will return the stored value and drop variables captured
+    /// by the closure instead of leaking their owned resources.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// #![feature(drop_guard)]
+    ///
+    /// use std::mem::DropGuard;
+    ///
+    /// let value = String::from("Nori likes chicken");
+    /// let guard = DropGuard::new(value, |s| println!("{s}"));
+    /// assert_eq!(DropGuard::into_inner(guard), "Nori likes chicken");
+    /// ```
+    #[unstable(feature = "drop_guard", issue = "144426")]
+    #[inline]
+    pub fn into_inner(guard: Self) -> T {
+        // First we ensure that dropping the guard will not trigger
+        // its destructor
+        let mut guard = ManuallyDrop::new(guard);
+
+        // Next we manually read the stored value from the guard.
+        //
+        // SAFETY: this is safe because we've taken ownership of the guard.
+        let value = unsafe { ManuallyDrop::take(&mut guard.inner) };
+
+        // Finally we drop the stored closure. We do this *after* having read
+        // the value, so that even if the closure's `drop` function panics,
+        // unwinding still tries to drop the value.
+        //
+        // SAFETY: this is safe because we've taken ownership of the guard.
+        unsafe { ManuallyDrop::drop(&mut guard.f) };
+        value
+    }
+}
+
+#[unstable(feature = "drop_guard", issue = "144426")]
+impl<T, F> Deref for DropGuard<T, F>
+where
+    F: FnOnce(T),
+{
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        &*self.inner
+    }
+}
+
+#[unstable(feature = "drop_guard", issue = "144426")]
+impl<T, F> DerefMut for DropGuard<T, F>
+where
+    F: FnOnce(T),
+{
+    fn deref_mut(&mut self) -> &mut T {
+        &mut *self.inner
+    }
+}
+
+#[unstable(feature = "drop_guard", issue = "144426")]
+impl<T, F> Drop for DropGuard<T, F>
+where
+    F: FnOnce(T),
+{
+    fn drop(&mut self) {
+        // SAFETY: `DropGuard` is in the process of being dropped.
+        let inner = unsafe { ManuallyDrop::take(&mut self.inner) };
+
+        // SAFETY: `DropGuard` is in the process of being dropped.
+        let f = unsafe { ManuallyDrop::take(&mut self.f) };
+
+        f(inner);
+    }
+}
+
+#[unstable(feature = "drop_guard", issue = "144426")]
+impl<T, F> Debug for DropGuard<T, F>
+where
+    T: Debug,
+    F: FnOnce(T),
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(&**self, f)
+    }
+}
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 33407637ab3..db4c8e9e551 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -21,6 +21,10 @@ mod transmutability;
 #[unstable(feature = "transmutability", issue = "99571")]
 pub use transmutability::{Assume, TransmuteFrom};
 
+mod drop_guard;
+#[unstable(feature = "drop_guard", issue = "144426")]
+pub use drop_guard::DropGuard;
+
 // 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.
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index dbe3999b4a4..1a2a5182567 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -974,9 +974,10 @@ pub const fn dangling_mut<T>() -> *mut T {
 #[must_use]
 #[inline(always)]
 #[stable(feature = "exposed_provenance", since = "1.84.0")]
+#[rustc_const_unstable(feature = "const_exposed_provenance", issue = "144538")]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
-pub fn with_exposed_provenance<T>(addr: usize) -> *const T {
+pub const fn with_exposed_provenance<T>(addr: usize) -> *const T {
     addr as *const T
 }
 
@@ -1014,9 +1015,10 @@ pub fn with_exposed_provenance<T>(addr: usize) -> *const T {
 #[must_use]
 #[inline(always)]
 #[stable(feature = "exposed_provenance", since = "1.84.0")]
+#[rustc_const_unstable(feature = "const_exposed_provenance", issue = "144538")]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead
-pub fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T {
+pub const fn with_exposed_provenance_mut<T>(addr: usize) -> *mut T {
     addr as *mut T
 }
 
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 1f34a7931d2..14042997bc2 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -1230,7 +1230,7 @@ impl<T> [T] {
     ///
     /// [`chunks`]: slice::chunks
     /// [`rchunks_exact`]: slice::rchunks_exact
-    /// [`as_chunks`]: slice::chunks
+    /// [`as_chunks`]: slice::as_chunks
     #[stable(feature = "chunks_exact", since = "1.31.0")]
     #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")]
     #[inline]
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index 57bea505433..70c02ead358 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -1245,8 +1245,8 @@ impl AtomicBool {
     /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the
     /// atomic types work with interior mutability. All modifications of an atomic change the value
     /// through a shared reference, and can do so safely as long as they use atomic operations. Any
-    /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same
-    /// restriction in [Memory model for atomic accesses].
+    /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the
+    /// requirements of the [memory model].
     ///
     /// # Examples
     ///
@@ -1265,7 +1265,7 @@ impl AtomicBool {
     /// # }
     /// ```
     ///
-    /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses
+    /// [memory model]: self#memory-model-for-atomic-accesses
     #[inline]
     #[stable(feature = "atomic_as_ptr", since = "1.70.0")]
     #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
@@ -2489,8 +2489,8 @@ impl<T> AtomicPtr<T> {
     /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the
     /// atomic types work with interior mutability. All modifications of an atomic change the value
     /// through a shared reference, and can do so safely as long as they use atomic operations. Any
-    /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same
-    /// restriction in [Memory model for atomic accesses].
+    /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the
+    /// requirements of the [memory model].
     ///
     /// # Examples
     ///
@@ -2510,7 +2510,7 @@ impl<T> AtomicPtr<T> {
     /// }
     /// ```
     ///
-    /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses
+    /// [memory model]: self#memory-model-for-atomic-accesses
     #[inline]
     #[stable(feature = "atomic_as_ptr", since = "1.70.0")]
     #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
@@ -3623,8 +3623,8 @@ macro_rules! atomic_int {
             /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the
             /// atomic types work with interior mutability. All modifications of an atomic change the value
             /// through a shared reference, and can do so safely as long as they use atomic operations. Any
-            /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same
-            /// restriction in [Memory model for atomic accesses].
+            /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the
+            /// requirements of the [memory model].
             ///
             /// # Examples
             ///
@@ -3645,7 +3645,7 @@ macro_rules! atomic_int {
             /// # }
             /// ```
             ///
-            /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses
+            /// [memory model]: self#memory-model-for-atomic-accesses
             #[inline]
             #[stable(feature = "atomic_as_ptr", since = "1.70.0")]
             #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs
index 57a89365fa9..029a7b00ad3 100644
--- a/library/coretests/tests/lib.rs
+++ b/library/coretests/tests/lib.rs
@@ -29,6 +29,7 @@
 #![feature(core_private_diy_float)]
 #![feature(cstr_display)]
 #![feature(dec2flt)]
+#![feature(drop_guard)]
 #![feature(duration_constants)]
 #![feature(duration_constructors)]
 #![feature(duration_constructors_lite)]
diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs
index 9c15be4a8c4..e896c61ef48 100644
--- a/library/coretests/tests/mem.rs
+++ b/library/coretests/tests/mem.rs
@@ -1,5 +1,6 @@
 use core::mem::*;
 use core::{array, ptr};
+use std::cell::Cell;
 #[cfg(panic = "unwind")]
 use std::rc::Rc;
 
@@ -795,3 +796,48 @@ fn const_maybe_uninit_zeroed() {
 
     assert_eq!(unsafe { (*UNINIT.0.cast::<[[u8; SIZE]; 1]>())[0] }, [0u8; SIZE]);
 }
+
+#[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 closure_drops = Cell::new(0);
+    let guard = DropGuard::new(value, |_| closure_drops.set(1 + closure_drops.get()));
+    assert_eq!(value_drops.get(), 0);
+    assert_eq!(closure_drops.get(), 0);
+    drop(guard);
+    assert_eq!(value_drops.get(), 1);
+    assert_eq!(closure_drops.get(), 1);
+}
+
+#[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 inner = DropGuard::into_inner(guard);
+    assert_eq!(dropped.get(), false);
+    assert_eq!(*inner, 42);
+}
+
+#[test]
+#[cfg(panic = "unwind")]
+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);
+
+    // Create a closure that will begin unwinding when dropped.
+    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);
+        DropGuard::into_inner(guard);
+    }));
+    assert!(value_was_dropped);
+}
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 433e013b40f..77301d7228e 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -330,6 +330,7 @@
 #![feature(clone_to_uninit)]
 #![feature(core_intrinsics)]
 #![feature(core_io_borrowed_buf)]
+#![feature(drop_guard)]
 #![feature(duration_constants)]
 #![feature(error_generic_member_access)]
 #![feature(error_iter)]