rust-lang/rust · #161702

Use `drop_guard` in some places in {core,alloc,std}

GrigorenkoPV · merged Aug 31, 202619 files · 313 + / 506
library/alloc/src/boxed/thin.rs13 + / 27
@@ -11,7 +11,7 @@ use core::marker::PhantomData; use core::marker::Unsize; #[cfg(not(no_global_oom_handling))] use core::mem;-use core::mem::SizedTypeProperties;+use core::mem::{DropGuard, SizedTypeProperties}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull, Pointee}; @@ -364,38 +364,24 @@ impl<H> WithHeader<H> {     // - Assumes that either `value` can be dereferenced, or is the     //   `NonNull::dangling()` we use when both `T` and `H` are ZSTs.     unsafe fn drop<T: ?Sized>(&self, value: *mut T) {-        struct DropGuard<H> {-            ptr: NonNull<u8>,-            value_layout: Layout,-            _marker: PhantomData<H>,-        }--        impl<H> Drop for DropGuard<H> {-            fn drop(&mut self) {-                // All ZST are allocated statically.-                if self.value_layout.size() == 0 {-                    return;-                }+        // SAFETY: Caller ensures `value` is valid.+        let value_layout = unsafe { Layout::for_value_raw(value) }; -                let (layout, value_offset) =-                    // SAFETY: Layout must have been computable if we're in drop-                    unsafe { WithHeader::<H>::alloc_layout(self.value_layout).unwrap_unchecked() };+        let _guard; +        // All ZST are allocated statically.+        if value_layout.size() != 0 {+            _guard = DropGuard::new(self.0, |ptr| {+                let layout = WithHeader::<H>::alloc_layout(value_layout);+                // SAFETY: Layout must have been computable if we're in this callback+                let (layout, value_offset) = unsafe { layout.unwrap_unchecked() };                 // Since we only allocate for non-ZSTs, the layout size cannot be zero.-                debug_assert!(layout.size() != 0);+                debug_assert_ne!(layout.size(), 0);                 // SAFETY: We own the allocation with `layout` at `ptr - value_offset`.-                unsafe { alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout) };-            }+                unsafe { alloc::dealloc(ptr.as_ptr().sub(value_offset), layout) };+            });         } -        // `_guard` will deallocate the memory when dropped, even if `drop_in_place` unwinds.-        let _guard = DropGuard {-            ptr: self.0,-            // SAFETY: Caller ensures `value` is valid.-            value_layout: unsafe { Layout::for_value_raw(value) },-            _marker: PhantomData::<H>,-        };-         // We only drop the value because the Pointee trait requires that the metadata is copy         // aka trivially droppable.         // SAFETY: We're the only droppers of `value` and it's not dropped again.
library/alloc/src/collections/binary_heap/mod.rs3 + / 11
@@ -145,7 +145,7 @@  use core::alloc::Allocator; use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen};-use core::mem::{self, ManuallyDrop, swap};+use core::mem::{DropGuard, ManuallyDrop, swap}; use core::num::NonZero; use core::ops::{Deref, DerefMut}; use core::{fmt, ptr};@@ -1914,18 +1914,10 @@ impl<'a, T: Ord, A: Allocator> DrainSorted<'a, T, A> { impl<'a, T: Ord, A: Allocator> Drop for DrainSorted<'a, T, A> {     /// Removes heap elements in heap order.     fn drop(&mut self) {-        struct DropGuard<'r, 'a, T: Ord, A: Allocator>(&'r mut DrainSorted<'a, T, A>);--        impl<'r, 'a, T: Ord, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {-            fn drop(&mut self) {-                while self.0.inner.pop().is_some() {}-            }-        }-         while let Some(item) = self.inner.pop() {-            let guard = DropGuard(self);+            let guard = DropGuard::new(&mut *self, |this| while this.inner.pop().is_some() {});             drop(item);-            mem::forget(guard);+            DropGuard::dismiss(guard);         }     } }
library/alloc/src/collections/btree/map.rs6 + / 12
@@ -5,7 +5,7 @@ use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData;-use core::mem::{self, ManuallyDrop};+use core::mem::{self, DropGuard, ManuallyDrop}; use core::ops::{Bound, Index, RangeBounds}; use core::ptr; @@ -1912,24 +1912,18 @@ impl<K, V, A: AllocatorClone> IntoIterator for BTreeMap<K, V, A> { #[stable(feature = "btree_drop", since = "1.7.0")] impl<K, V, A: AllocatorClone> Drop for IntoIter<K, V, A> {     fn drop(&mut self) {-        struct DropGuard<'a, K, V, A: AllocatorClone>(&'a mut IntoIter<K, V, A>);--        impl<'a, K, V, A: AllocatorClone> Drop for DropGuard<'a, K, V, A> {-            fn drop(&mut self) {+        while let Some(kv) = self.dying_next() {+            let guard = DropGuard::new(&mut *self, |this| {                 // Continue the same loop we perform below. This only runs when unwinding, so we                 // don't have to care about panics this time (they'll abort).-                while let Some(kv) = self.0.dying_next() {+                while let Some(kv) = this.dying_next() {                     // SAFETY: we consume the dying handle immediately.                     unsafe { kv.drop_key_val() };                 }-            }-        }--        while let Some(kv) = self.dying_next() {-            let guard = DropGuard(self);+            });             // SAFETY: we don't touch the tree before consuming the dying handle.             unsafe { kv.drop_key_val() };-            mem::forget(guard);+            DropGuard::dismiss(guard);         }     } }
library/alloc/src/collections/btree/mem.rs2 + / 8
@@ -16,20 +16,14 @@ pub(super) fn take_mut<T>(v: &mut T, change: impl FnOnce(T) -> T) { /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] pub(super) fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {-    struct PanicGuard;-    impl Drop for PanicGuard {-        fn drop(&mut self) {-            intrinsics::abort()-        }-    }-    let guard = PanicGuard;+    let guard = mem::DropGuard::new((), |()| intrinsics::abort());     // SAFETY: v is valid for reads and we write a new value before returning.     let value = unsafe { ptr::read(v) };     let (new_value, ret) = change(value);     // SAFETY: new_value is T and v is valid for writes.     unsafe {         ptr::write(v, new_value);     }-    mem::forget(guard);+    mem::DropGuard::dismiss(guard);     ret }
library/alloc/src/collections/btree/node.rs3 + / 14
@@ -32,7 +32,7 @@ //   an edge both identifies a position and contains a pointer to a child node.  use core::marker::PhantomData;-use core::mem::{self, MaybeUninit};+use core::mem::{self, DropGuard, MaybeUninit}; use core::num::NonZero; use core::ptr::{self, NonNull}; use core::slice::SliceIndex;@@ -1237,25 +1237,14 @@ impl<K, V, NodeType> Handle<NodeRef<marker::Dying, K, V, NodeType>, marker::KV>     /// The node that the handle refers to must not yet have been deallocated.     #[inline]     pub(super) unsafe fn drop_key_val(mut self) {-        // Run the destructor of the value even if the destructor of the key panics.-        struct Dropper<'a, T>(&'a mut MaybeUninit<T>);-        impl<T> Drop for Dropper<'_, T> {-            #[inline]-            fn drop(&mut self) {-                // ignore-tidy-undocumented-unsafe-                unsafe {-                    self.0.assume_init_drop();-                }-            }-        }-         debug_assert!(self.idx < self.node.len());         let leaf = self.node.as_leaf_dying();         // ignore-tidy-undocumented-unsafe         unsafe {             let key = leaf.keys.get_unchecked_mut(self.idx);             let val = leaf.vals.get_unchecked_mut(self.idx);-            let _guard = Dropper(val);+            // Run the destructor of the value even if the destructor of the key panics.+            let _guard = DropGuard::new(val, |val| val.assume_init_drop());             key.assume_init_drop();             // dropping the guard will drop the value         }
library/alloc/src/collections/linked_list.rs9 + / 13
@@ -17,6 +17,7 @@ use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData;+use core::mem::DropGuard; use core::ptr::NonNull; use core::{fmt, mem}; @@ -1192,20 +1193,15 @@ impl<T, A: Allocator> LinkedList<T, A> { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList<T, A> {     fn drop(&mut self) {-        struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList<T, A>);--        impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> {-            fn drop(&mut self) {-                // Continue the same loop we do below. This only runs when a destructor has-                // panicked. If another one panics this will abort.-                while self.0.pop_front_node().is_some() {}-            }-        }-         // Wrap self so that if a destructor panics, we can try to keep looping-        let guard = DropGuard(self);-        while guard.0.pop_front_node().is_some() {}-        mem::forget(guard);+        let mut guard = DropGuard::new(self, |this| {+            // Continue the same loop we do below. This only runs when a destructor has+            // panicked. If another one panics this will abort.+            while this.pop_front_node().is_some() {}+        });++        while guard.pop_front_node().is_some() {}+        DropGuard::dismiss(guard);     } } 
library/alloc/src/collections/vec_deque/drain.rs120 + / 127
@@ -1,6 +1,6 @@ use core::iter::FusedIterator; use core::marker::PhantomData;-use core::mem::{self, SizedTypeProperties};+use core::mem::{self, DropGuard, SizedTypeProperties}; use core::ptr::NonNull; use core::{fmt, ptr}; @@ -94,144 +94,137 @@ unsafe impl<T: Send, A: Allocator + Send> Send for Drain<'_, T, A> {} #[stable(feature = "drain", since = "1.6.0")] impl<T, A: Allocator> Drop for Drain<'_, T, A> {     fn drop(&mut self) {-        struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>);--        let guard = DropGuard(self);--        if mem::needs_drop::<T>() && guard.0.remaining != 0 {-            // SAFETY: We just checked that `self.remaining != 0`.-            let (front, back) = unsafe { guard.0.as_slices() };-            // since idx is a logical index, we don't need to worry about wrapping.-            guard.0.idx += front.len();-            guard.0.remaining -= front.len();-            // SAFETY: This can't have been dropped before since-            // `idx` & `remaining` track what's been dropped.-            unsafe { ptr::drop_in_place(front) };-            guard.0.remaining = 0;-            // SAFETY: Ditto.-            unsafe { ptr::drop_in_place(back) };-        }-         // Dropping `guard` handles moving the remaining elements into place.-        impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {-            #[inline]-            fn drop(&mut self) {-                if mem::needs_drop::<T>() && self.0.remaining != 0 {-                    // SAFETY: We just checked that `self.remaining != 0`.-                    unsafe {-                        let (front, back) = self.0.as_slices();-                        ptr::drop_in_place(front);-                        ptr::drop_in_place(back);-                    }+        let mut guard = DropGuard::new(self, |drain| {+            if mem::needs_drop::<T>() && drain.remaining != 0 {+                // SAFETY: We just checked that `self.remaining != 0`.+                unsafe {+                    let (front, back) = drain.as_slices();+                    ptr::drop_in_place(front);+                    ptr::drop_in_place(back);                 }+            } -                // ignore-tidy-undocumented-unsafe-                let source_deque = unsafe { self.0.deque.as_mut() };+            // ignore-tidy-undocumented-unsafe+            let source_deque = unsafe { drain.deque.as_mut() }; -                let drain_len = self.0.drain_len;-                let head_len = source_deque.len; // #elements in front of the drain-                let tail_len = self.0.tail_len; // #elements behind the drain-                let new_len = head_len + tail_len;+            let drain_len = drain.drain_len;+            let head_len = source_deque.len; // #elements in front of the drain+            let tail_len = drain.tail_len; // #elements behind the drain+            let new_len = head_len + tail_len; -                if T::IS_ZST {-                    // no need to copy around any memory if T is a ZST-                    source_deque.len = new_len;-                    return;-                }+            if T::IS_ZST {+                // no need to copy around any memory if T is a ZST+                source_deque.len = new_len;+                return;+            } -                // Next, we will fill the hole left by the drain with as few writes as possible.-                // The code below handles the following control flow and reduces the amount of-                // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e.-                // draining at the front or at the back of the dequeue is especially common.-                //-                // H = "head index" = `deque.head`-                // h = elements in front of the drain-                // d = elements in the drain-                // t = elements behind the drain-                //-                // Note that the buffer may wrap at any point and the wrapping is handled by-                // `wrap_copy` and `to_physical_idx`.-                //-                // Case 1: if `head_len == 0 && tail_len == 0`-                // Everything was drained, reset the head index back to 0.-                //             H-                // [ . . . . . d d d d . . . . . ]-                //   H-                // [ . . . . . . . . . . . . . . ]-                //-                // Case 2: else if `tail_len == 0`-                // Don't move data or the head index.-                //         H-                // [ . . . h h h h d d d d . . . ]-                //         H-                // [ . . . h h h h . . . . . . . ]-                //-                // Case 3: else if `head_len == 0`-                // Don't move data, but move the head index.-                //         H-                // [ . . . d d d d t t t t . . . ]-                //                 H-                // [ . . . . . . . t t t t . . . ]-                //-                // Case 4: else if `tail_len <= head_len`-                // Move data, but not the head index.-                //       H-                // [ . . h h h h d d d d t t . . ]-                //       H-                // [ . . h h h h t t . . . . . . ]-                //-                // Case 5: else-                // Move data and the head index.-                //       H-                // [ . . h h d d d d t t t t . . ]-                //               H-                // [ . . . . . . h h t t t t . . ]+            // Next, we will fill the hole left by the drain with as few writes as possible.+            // The code below handles the following control flow and reduces the amount of+            // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e.+            // draining at the front or at the back of the dequeue is especially common.+            //+            // H = "head index" = `deque.head`+            // h = elements in front of the drain+            // d = elements in the drain+            // t = elements behind the drain+            //+            // Note that the buffer may wrap at any point and the wrapping is handled by+            // `wrap_copy` and `to_physical_idx`.+            //+            // Case 1: if `head_len == 0 && tail_len == 0`+            // Everything was drained, reset the head index back to 0.+            //             H+            // [ . . . . . d d d d . . . . . ]+            //   H+            // [ . . . . . . . . . . . . . . ]+            //+            // Case 2: else if `tail_len == 0`+            // Don't move data or the head index.+            //         H+            // [ . . . h h h h d d d d . . . ]+            //         H+            // [ . . . h h h h . . . . . . . ]+            //+            // Case 3: else if `head_len == 0`+            // Don't move data, but move the head index.+            //         H+            // [ . . . d d d d t t t t . . . ]+            //                 H+            // [ . . . . . . . t t t t . . . ]+            //+            // Case 4: else if `tail_len <= head_len`+            // Move data, but not the head index.+            //       H+            // [ . . h h h h d d d d t t . . ]+            //       H+            // [ . . h h h h t t . . . . . . ]+            //+            // Case 5: else+            // Move data and the head index.+            //       H+            // [ . . h h d d d d t t t t . . ]+            //               H+            // [ . . . . . . h h t t t t . . ] -                // When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`),-                // we don't need to copy any data. The number of elements copied would be 0.-                if head_len != 0 && tail_len != 0 {-                    join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len);-                    // Marking this function as cold helps LLVM to eliminate it entirely if-                    // this branch is never taken.-                    // We use `#[cold]` instead of `#[inline(never)]`, because inlining this-                    // function into the general case (`.drain(n..m)`) is fine.-                    // See `tests/codegen-llvm/vecdeque-drain.rs` for a test.-                    #[cold]-                    fn join_head_and_tail_wrapping<T, A: Allocator>(-                        source_deque: &mut VecDeque<T, A>,-                        drain_len: usize,-                        head_len: usize,-                        tail_len: usize,-                    ) {-                        // Pick whether to move the head or the tail here.-                        let (src, dst, len);-                        if head_len < tail_len {-                            src = source_deque.head;-                            dst = source_deque.to_wrapped_index(drain_len);-                            len = head_len;-                        } else {-                            src = source_deque.to_wrapped_index(head_len + drain_len);-                            dst = source_deque.to_wrapped_index(head_len);-                            len = tail_len;-                        };+            // When draining at the front (`.drain(..n)`) or at the back (`.drain(n..)`),+            // we don't need to copy any data. The number of elements copied would be 0.+            if head_len != 0 && tail_len != 0 {+                join_head_and_tail_wrapping(source_deque, drain_len, head_len, tail_len);+                // Marking this function as cold helps LLVM to eliminate it entirely if+                // this branch is never taken.+                // We use `#[cold]` instead of `#[inline(never)]`, because inlining this+                // function into the general case (`.drain(n..m)`) is fine.+                // See `tests/codegen-llvm/vecdeque-drain.rs` for a test.+                #[cold]+                fn join_head_and_tail_wrapping<T, A: Allocator>(+                    source_deque: &mut VecDeque<T, A>,+                    drain_len: usize,+                    head_len: usize,+                    tail_len: usize,+                ) {+                    // Pick whether to move the head or the tail here.+                    let (src, dst, len);+                    if head_len < tail_len {+                        src = source_deque.head;+                        dst = source_deque.to_wrapped_index(drain_len);+                        len = head_len;+                    } else {+                        src = source_deque.to_wrapped_index(head_len + drain_len);+                        dst = source_deque.to_wrapped_index(head_len);+                        len = tail_len;+                    }; -                        // ignore-tidy-undocumented-unsafe-                        unsafe {-                            source_deque.wrap_copy(src, dst, len);-                        }+                    // ignore-tidy-undocumented-unsafe+                    unsafe {+                        source_deque.wrap_copy(src, dst, len);                     }                 }+            } -                if new_len == 0 {-                    // Special case: If the entire deque was drained, reset the head back to 0,-                    // like `.clear()` does.-                    source_deque.head = WrappedIndex::zero();-                } else if head_len < tail_len {-                    // If we moved the head above, then we need to adjust the head index here.-                    source_deque.head = source_deque.to_wrapped_index(drain_len);-                }-                source_deque.len = new_len;+            if new_len == 0 {+                // Special case: If the entire deque was drained, reset the head back to 0,+                // like `.clear()` does.+                source_deque.head = WrappedIndex::zero();+            } else if head_len < tail_len {+                // If we moved the head above, then we need to adjust the head index here.+                source_deque.head = source_deque.to_wrapped_index(drain_len);             }+            source_deque.len = new_len;+        });++        if mem::needs_drop::<T>() && guard.remaining != 0 {+            // SAFETY: We just checked that `self.remaining != 0`.+            let (front, back) = unsafe { guard.as_slices() };+            // since idx is a logical index, we don't need to worry about wrapping.+            guard.idx += front.len();+            guard.remaining -= front.len();+            // SAFETY: This can't have been dropped before since+            // `idx` & `remaining` track what's been dropped.+            unsafe { ptr::drop_in_place(front) };+            guard.remaining = 0;+            // SAFETY: Ditto.+            unsafe { ptr::drop_in_place(back) };         }     } }
library/alloc/src/collections/vec_deque/into_iter.rs19 + / 35
@@ -1,5 +1,5 @@ use core::iter::{FusedIterator, TrustedLen};-use core::mem::MaybeUninit;+use core::mem::{DropGuard, MaybeUninit}; use core::num::NonZero; use core::ops::Try; use core::{array, fmt, ptr};@@ -78,28 +78,20 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {         F: FnMut(B, Self::Item) -> R,         R: Try<Output = B>,     {-        struct Guard<'a, T, A: Allocator> {-            deque: &'a mut VecDeque<T, A>,-            // `consumed <= deque.len` always holds.-            consumed: usize,-        }--        impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> {-            fn drop(&mut self) {-                self.deque.len -= self.consumed;-                self.deque.head = self.deque.to_wrapped_index(self.consumed);-            }-        }--        let mut guard = Guard { deque: &mut self.inner, consumed: 0 };+        // `consumed <= deque.len` always holds.+        let mut guard = DropGuard::new((&mut self.inner, 0), |(deque, consumed)| {+            deque.len -= consumed;+            deque.head = deque.to_wrapped_index(consumed);+        }); -        let (head, tail) = guard.deque.as_slices();+        let (deque, consumed) = &mut *guard;+        let (head, tail) = deque.as_slices();          init = head             .iter()             .map(|elem| {-                guard.consumed += 1;-                // SAFETY: Because we incremented `guard.consumed`, the+                *consumed += 1;+                // SAFETY: Because we incremented `consumed`, the                 // deque effectively forgot the element, so we can take                 // ownership                 unsafe { ptr::read(elem) }@@ -108,7 +100,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {          tail.iter()             .map(|elem| {-                guard.consumed += 1;+                *consumed += 1;                 // SAFETY: Same as above.                 unsafe { ptr::read(elem) }             })@@ -201,34 +193,26 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {         F: FnMut(B, Self::Item) -> R,         R: Try<Output = B>,     {-        struct Guard<'a, T, A: Allocator> {-            deque: &'a mut VecDeque<T, A>,-            // `consumed <= deque.len` always holds.-            consumed: usize,-        }--        impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> {-            fn drop(&mut self) {-                self.deque.len -= self.consumed;-            }-        }--        let mut guard = Guard { deque: &mut self.inner, consumed: 0 };+        // `consumed <= deque.len` always holds.+        let mut guard = DropGuard::new((&mut self.inner, 0), |(deque, consumed)| {+            deque.len -= consumed;+        }); -        let (head, tail) = guard.deque.as_slices();+        let (deque, consumed) = &mut *guard;+        let (head, tail) = deque.as_slices();          init = tail             .iter()             .map(|elem| {-                guard.consumed += 1;+                *consumed += 1;                 // SAFETY: See `try_fold`'s safety comment.                 unsafe { ptr::read(elem) }             })             .try_rfold(init, &mut f)?;          head.iter()             .map(|elem| {-                guard.consumed += 1;+                *consumed += 1;                 // SAFETY: Same as above.                 unsafe { ptr::read(elem) }             })
library/alloc/src/collections/vec_deque/mod.rs9 + / 21
@@ -17,7 +17,7 @@ use core::iter::{ByRefSized, repeat_n, repeat_with}; // failures in linkchecker even though rustdoc built the docs just fine. #[allow(unused_imports)] use core::mem;-use core::mem::{ManuallyDrop, SizedTypeProperties};+use core::mem::{DropGuard, ManuallyDrop, SizedTypeProperties}; use core::ops::{Index, IndexMut, Range, RangeBounds}; use core::{fmt, ptr, slice}; @@ -653,37 +653,25 @@ impl<T, A: Allocator> VecDeque<T, A> {         mut iter: impl Iterator<Item = T>,         len: usize,     ) -> usize {-        struct Guard<'a, T, A: Allocator> {-            deque: &'a mut VecDeque<T, A>,-            written: usize,-        }--        impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> {-            fn drop(&mut self) {-                self.deque.len += self.written;-            }-        }-         let head_room = self.capacity() - dst.as_index(); -        let mut guard = Guard { deque: self, written: 0 };+        let mut guard = DropGuard::new((self, 0), |(deque, written)| {+            deque.len += written;+        });+        let (deque, written) = &mut *guard;          if head_room >= len {             // ignore-tidy-undocumented-unsafe-            unsafe { guard.deque.write_iter(dst, iter, &mut guard.written) };+            unsafe { deque.write_iter(dst, iter, written) };         } else {             // ignore-tidy-undocumented-unsafe             unsafe {-                guard.deque.write_iter(-                    dst,-                    ByRefSized(&mut iter).take(head_room),-                    &mut guard.written,-                );-                guard.deque.write_iter(WrappedIndex::zero(), iter, &mut guard.written)+                deque.write_iter(dst, ByRefSized(&mut iter).take(head_room), written);+                deque.write_iter(WrappedIndex::zero(), iter, written)             };         } -        guard.written+        *written     }      /// Frobs the head and tail sections around to handle the fact that we
library/alloc/src/rc.rs13 + / 28
@@ -2450,47 +2450,32 @@ impl<T> Rc<[T]> {     /// Behavior is undefined should the size be wrong.     #[cfg(not(no_global_oom_handling))]     unsafe fn from_iter_exact(iter: impl Iterator<Item = T>, len: usize) -> Rc<[T]> {-        // Panic guard while cloning T elements.-        // In the event of a panic, elements that have been written-        // into the new RcInner will be dropped, then the memory freed.-        struct Guard<T> {-            mem: NonNull<u8>,-            elems: *mut T,-            layout: Layout,-            n_elems: usize,-        }--        impl<T> Drop for Guard<T> {-            fn drop(&mut self) {-                // ignore-tidy-undocumented-unsafe-                unsafe {-                    let slice = from_raw_parts_mut(self.elems, self.n_elems);-                    ptr::drop_in_place(slice);--                    Global.deallocate(self.mem, self.layout);-                }-            }-        }+        use core::mem::DropGuard;          // ignore-tidy-undocumented-unsafe         unsafe {             let ptr = Self::allocate_for_slice(len);--            let mem = ptr as *mut _ as *mut u8;             let layout = Layout::for_value_raw(ptr);              // Pointer to first element-            let elems = (&raw mut (*ptr).value) as *mut T;+            let elems = (&raw mut (*ptr).value).as_mut_ptr(); -            let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 };+            // Panic guard while cloning T elements.+            // In the event of a panic, elements that have been written+            // into the new RcInner will be dropped, then the memory freed.+            let mut guard = DropGuard::new(0, |n_elems| {+                let slice = from_raw_parts_mut(elems, n_elems);+                ptr::drop_in_place(slice);+                Global.deallocate(NonNull::new_unchecked(ptr.cast()), layout);+            });              for (i, item) in iter.enumerate() {                 ptr::write(elems.add(i), item);-                guard.n_elems += 1;+                *guard += 1;             } -            // All clear. Forget the guard so it doesn't free the new RcInner.-            mem::forget(guard);+            // All clear. Dismiss the guard so it doesn't free the new RcInner.+            DropGuard::dismiss(guard);              Self::from_ptr(ptr)         }
library/alloc/src/slice.rs15 + / 20
@@ -408,35 +408,30 @@ impl<T> [T] {         impl<T: Clone> ConvertVec for T {             #[inline]             default fn to_vec<A: Allocator>(s: &[Self], alloc: A) -> Vec<Self, A> {-                struct DropGuard<'a, T, A: Allocator> {-                    vec: &'a mut Vec<T, A>,-                    num_init: usize,-                }-                impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> {-                    #[inline]-                    fn drop(&mut self) {+                use core::mem::DropGuard;++                let mut guard = DropGuard::new(+                    (0, Vec::with_capacity_in(s.len(), alloc)),+                    |(num_init, mut vec)| {                         // SAFETY:                         // items were marked initialized in the loop below-                        unsafe {-                            self.vec.set_len(self.num_init);-                        }-                    }-                }-                let mut vec = Vec::with_capacity_in(s.len(), alloc);-                let mut guard = DropGuard { vec: &mut vec, num_init: 0 };-                let slots = guard.vec.spare_capacity_mut();+                        unsafe { vec.set_len(num_init) }+                    },+                );+                let (num_init, vec) = &mut *guard;++                let slots = vec.spare_capacity_mut();                 // .take(slots.len()) is necessary for LLVM to remove bounds checks                 // and has better codegen than zip.                 for (i, b) in s.iter().enumerate().take(slots.len()) {-                    guard.num_init = i;+                    *num_init = i;                     slots[i].write(b.clone());                 }-                core::mem::forget(guard);++                let (_, mut vec) = DropGuard::dismiss(guard);                 // SAFETY:                 // the vec was allocated and initialized above to at least this length.-                unsafe {-                    vec.set_len(s.len());-                }+                unsafe { vec.set_len(s.len()) };                 vec             }         }
library/alloc/src/string.rs13 + / 20
@@ -46,6 +46,7 @@ use core::error::Error; use core::iter::FusedIterator; #[cfg(not(no_global_oom_handling))] use core::iter::from_fn;+use core::mem::DropGuard; #[cfg(not(no_global_oom_handling))] use core::num::Saturating; #[cfg(not(no_global_oom_handling))]@@ -1689,20 +1690,6 @@ impl String {             return;         } -        struct PanicGuard<'a> {-            s: &'a mut String,-            write: usize,-        }--        impl Drop for PanicGuard<'_> {-            fn drop(&mut self) {-                debug_assert!(self.write <= self.s.len());-                debug_assert!(str::from_utf8(&self.s.vec[..self.write]).is_ok());-                // SAFETY: Restore the string length to the number of bytes written so far.-                unsafe { self.s.vec.set_len(self.write) }-            }-        }-         // Fast path: find the first character that should be removed or return early.         let mut chars = self.char_indices();         let (mut read, write) = loop {@@ -1714,26 +1701,32 @@ impl String {         drop(chars);          // Slow path: at least one character is going to be removed.-        let mut g = PanicGuard { s: self, write };+        let mut guard = DropGuard::new((self, write), |(s, write)| {+            debug_assert!(write <= s.len());+            debug_assert!(str::from_utf8(&s.vec[..write]).is_ok());+            // SAFETY: Restore the string length to the number of bytes written so far.+            unsafe { s.vec.set_len(write) }+        });+        let (s, write) = &mut *guard;         while read < len {             // SAFETY: `read` is within bound because `read` < `len`, so taking             // a slice with `len` is safe.-            let ch = unsafe { g.s.get_unchecked(read..len).chars().next().unwrap_unchecked() };+            let ch = unsafe { s.get_unchecked(read..len).chars().next().unwrap_unchecked() };             let ch_len = ch.len_utf8();             if f(ch) {                 // SAFETY: `read` is on a char boundary, as guaranteed above; `g.write` is                 // within bounds because it is always behind `read`.                 unsafe {-                    let ptr = g.s.vec.as_mut_ptr();-                    ptr::copy(ptr.add(read), ptr.add(g.write), ch_len);+                    let ptr = s.vec.as_mut_ptr();+                    ptr::copy(ptr.add(read), ptr.add(*write), ch_len);                 }-                g.write += ch_len;+                *write += ch_len;             }             read += ch_len;         }          // All bytes processed; commit the final length by dropping the guard.-        drop(g);+        drop(guard);     }      /// Inserts a character into this `String` at byte position `idx`.
library/alloc/src/sync.rs17 + / 39
@@ -19,6 +19,8 @@ use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize};+#[cfg(not(no_global_oom_handling))]+use core::mem::DropGuard; use core::mem::{self, Alignment, ManuallyDrop}; use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver};@@ -2417,47 +2419,31 @@ impl<T> Arc<[T]> {     /// Behavior is undefined should the size be wrong.     #[cfg(not(no_global_oom_handling))]     unsafe fn from_iter_exact(iter: impl Iterator<Item = T>, len: usize) -> Arc<[T]> {-        // Panic guard while cloning T elements.-        // In the event of a panic, elements that have been written-        // into the new ArcInner will be dropped, then the memory freed.-        struct Guard<T> {-            mem: NonNull<u8>,-            elems: *mut T,-            layout: Layout,-            n_elems: usize,-        }--        impl<T> Drop for Guard<T> {-            fn drop(&mut self) {-                // ignore-tidy-undocumented-unsafe-                unsafe {-                    let slice = from_raw_parts_mut(self.elems, self.n_elems);-                    ptr::drop_in_place(slice);--                    Global.deallocate(self.mem, self.layout);-                }-            }-        }-         // ignore-tidy-undocumented-unsafe         unsafe {             let ptr = Self::allocate_for_slice(len);--            let mem = ptr as *mut _ as *mut u8;             let layout = Layout::for_value_raw(ptr);              // Pointer to first element-            let elems = (&raw mut (*ptr).data) as *mut T;+            let elems = (&raw mut (*ptr).data).as_mut_ptr();++            // Panic guard while cloning T elements.+            // In the event of a panic, elements that have been written+            // into the new ArcInner will be dropped, then the memory freed.+            let mut guard = DropGuard::new(0, |n_elems| {+                let slice = from_raw_parts_mut(elems, n_elems);+                ptr::drop_in_place(slice); -            let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 };+                Global.deallocate(NonNull::new_unchecked(ptr.cast()), layout);+            });              for (i, item) in iter.enumerate() {                 ptr::write(elems.add(i), item);-                guard.n_elems += 1;+                *guard += 1;             } -            // All clear. Forget the guard so it doesn't free the new ArcInner.-            mem::forget(guard);+            // All clear. Dismiss the guard so it doesn't free the new ArcInner.+            DropGuard::dismiss(guard);              Self::from_ptr(ptr)         }@@ -2678,15 +2664,7 @@ impl<T: ?Sized + CloneToUninit, A: AllocatorClone> Arc<T, A> {             // If we unwind before the Arc is overwritten, we expose a strong             // count of 0, resulting in a UAF (#155746, #157203).             // Until the new Arc is written, the old Arc must remain valid-            struct Guard<'a, T: ?Sized> {-                inner: &'a ArcInner<T>,-            }-            impl<'a, T: ?Sized> Drop for Guard<'a, T> {-                fn drop(&mut self) {-                    self.inner.strong.store(1, Release);-                }-            }-            let guard = Guard { inner: this.inner() };+            let guard = DropGuard::new(this.inner(), |inner| inner.strong.store(1, Release));              // Can just steal the data, all that's left is Weaks             // Note that this can panic in two ways:@@ -2707,7 +2685,7 @@ impl<T: ?Sized + CloneToUninit, A: AllocatorClone> Arc<T, A> {                 );                  // We are now safe from panics.-                mem::forget(guard);+                DropGuard::dismiss(guard);                  // Materialize our own implicit weak pointer, so that it can clean                 // up the ArcInner as needed.
library/alloc/src/vec/drain.rs18 + / 25
@@ -1,5 +1,5 @@ use core::iter::{FusedIterator, TrustedLen};-use core::mem::{self, ManuallyDrop, SizedTypeProperties};+use core::mem::{self, DropGuard, ManuallyDrop, SizedTypeProperties}; use core::ptr::{self, NonNull}; use core::{fmt, slice}; @@ -176,29 +176,6 @@ impl<T, A: Allocator> DoubleEndedIterator for Drain<'_, T, A> { #[stable(feature = "drain", since = "1.6.0")] impl<T, A: Allocator> Drop for Drain<'_, T, A> {     fn drop(&mut self) {-        /// Moves back the un-`Drain`ed elements to restore the original `Vec`.-        struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>);--        impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {-            fn drop(&mut self) {-                if self.0.tail_len > 0 {-                    // ignore-tidy-undocumented-unsafe-                    unsafe {-                        let source_vec = self.0.vec.as_mut();-                        // memmove back untouched tail, update to new length-                        let start = source_vec.len();-                        let tail = self.0.tail_start;-                        if tail != start {-                            let src = source_vec.as_ptr().add(tail);-                            let dst = source_vec.as_mut_ptr().add(start);-                            ptr::copy(src, dst, self.0.tail_len);-                        }-                        source_vec.set_len(start + self.0.tail_len);-                    }-                }-            }-        }-         let iter = mem::take(&mut self.iter);         let drop_len = iter.len(); @@ -219,7 +196,23 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {         }          // ensure elements are moved back into their appropriate places, even when drop_in_place panics-        let _guard = DropGuard(self);+        let _guard = DropGuard::new(self, |this| {+            if this.tail_len > 0 {+                // ignore-tidy-undocumented-unsafe+                unsafe {+                    let source_vec = this.vec.as_mut();+                    // memmove back untouched tail, update to new length+                    let start = source_vec.len();+                    let tail = this.tail_start;+                    if tail != start {+                        let src = source_vec.as_ptr().add(tail);+                        let dst = source_vec.as_mut_ptr().add(start);+                        ptr::copy(src, dst, this.tail_len);+                    }+                    source_vec.set_len(start + this.tail_len);+                }+            }+        });          if drop_len == 0 {             return;
library/alloc/src/vec/into_iter.rs4 + / 16
@@ -3,7 +3,7 @@ use core::iter::{     TrustedRandomAccessNoCoerce, }; use core::marker::PhantomData;-use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties};+use core::mem::{DropGuard, ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::num::NonZero; #[cfg(not(no_global_oom_handling))] use core::ops::Deref;@@ -589,23 +589,11 @@ impl<T: Clone, A: Allocator + Clone> Clone for IntoIter<T, A> { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter<T, A> {     fn drop(&mut self) {-        struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter<T, A>);--        impl<T, A: Allocator> Drop for DropGuard<'_, T, A> {-            fn drop(&mut self) {-                // ignore-tidy-undocumented-unsafe-                unsafe {-                    self.0.dealloc_only();-                }-            }-        }--        let guard = DropGuard(self);+        // ignore-tidy-undocumented-unsafe+        let mut guard = DropGuard::new(self, |this| unsafe { this.dealloc_only() });         // destroy the remaining elements         // ignore-tidy-undocumented-unsafe-        unsafe {-            ptr::drop_in_place(guard.0.as_raw_mut_slice());-        }+        unsafe { ptr::drop_in_place(guard.as_raw_mut_slice()) }         // now `guard` will be dropped and do the rest     } }
library/std/src/sys/fs/unix.rs14 + / 22
@@ -2299,19 +2299,6 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { #[cfg(target_vendor = "apple")] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {     const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA;--    struct FreeOnDrop(libc::copyfile_state_t);-    impl Drop for FreeOnDrop {-        fn drop(&mut self) {-            // The code below ensures that `FreeOnDrop` is never a null pointer-            unsafe {-                // `copyfile_state_free` returns -1 if the `to` or `from` files-                // cannot be closed. However, this is not considered an error.-                libc::copyfile_state_free(self.0);-            }-        }-    }-     let (reader, reader_metadata) = open_from(from)?;      let clonefile_result = run_path_with_cstr(to, &|to| {@@ -2332,24 +2319,29 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {     // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.     let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; -    // We ensure that `FreeOnDrop` never contains a null pointer so it is+    let state = unsafe { libc::copyfile_state_alloc() };+    // We ensure that the guard never contains a null pointer so it is     // always safe to call `copyfile_state_free`-    let state = unsafe {-        let state = libc::copyfile_state_alloc();-        if state.is_null() {-            return Err(crate::io::Error::last_os_error());+    if state.is_null() {+        return Err(crate::io::Error::last_os_error());+    }+    let state = crate::mem::DropGuard::new(state, |state| {+        // SAFETY: just checked it's not null+        unsafe {+            // `copyfile_state_free` returns -1 if the `to` or `from` files+            // cannot be closed. However, this is not considered an error.+            libc::copyfile_state_free(state);         }-        FreeOnDrop(state)-    };+    });      let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA }; -    cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;+    cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), *state, flags) })?;      let mut bytes_copied: libc::off_t = 0;     cvt(unsafe {         libc::copyfile_state_get(-            state.0,+            *state,             libc::COPYFILE_STATE_COPIED as u32,             (&raw mut bytes_copied) as *mut libc::c_void,         )
library/std/src/sys/pal/unix/sync/condvar.rs8 + / 13
@@ -151,28 +151,23 @@ impl Condvar {     /// # Safety     /// May only be called once per instance of `Self`.     pub unsafe fn init(self: Pin<&mut Self>) {+        use crate::mem::DropGuard;         use crate::pin::pin; -        struct AttrGuard<'a>(Pin<&'a COpaque<libc::pthread_condattr_t>>);-        impl Drop for AttrGuard<'_> {-            fn drop(&mut self) {-                unsafe {-                    let result = libc::pthread_condattr_destroy(self.0.get());-                    assert_eq!(result, 0);-                }-            }-        }-         unsafe {             let attr = pin!(COpaque::<libc::pthread_condattr_t>::uninit());+             // FIXME(pin-ergonomics): remove the next line.             let attr = attr.into_ref();             let r = libc::pthread_condattr_init(attr.get());             assert_eq!(r, 0);-            let attr = AttrGuard(attr);-            let r = libc::pthread_condattr_setclock(attr.0.get(), Self::CLOCK);+            let attr = DropGuard::new(attr, |attr| {+                let result = libc::pthread_condattr_destroy(attr.get());+                assert_eq!(result, 0);+            });+            let r = libc::pthread_condattr_setclock(attr.get(), Self::CLOCK);             assert_eq!(r, 0);-            let r = libc::pthread_cond_init(self.as_ref().raw(), attr.0.get());+            let r = libc::pthread_cond_init(self.as_ref().raw(), attr.get());             assert_eq!(r, 0);         }     }
library/std/src/sys/process/unix/unix.rs23 + / 47
@@ -394,19 +394,11 @@ impl Command {         // want to be sure to restore the global environment back to what it         // once was, ensuring that our temporary override, when free'd, doesn't         // corrupt our process's environment.-        let mut _reset = None;+        let _reset;         if let Some(envp) = maybe_envp {-            struct Reset(*const *const libc::c_char);--            impl Drop for Reset {-                fn drop(&mut self) {-                    unsafe {-                        *sys::env::environ() = self.0;-                    }-                }-            }--            _reset = Some(Reset(*sys::env::environ()));+            _reset = core::mem::DropGuard::new(*sys::env::environ(), |prev| {+                *sys::env::environ() = prev;+            });             *sys::env::environ() = envp.as_ptr();         } @@ -461,8 +453,8 @@ impl Command {         #[cfg(target_os = "linux")]         use core::sync::atomic::{Atomic, AtomicU8, Ordering}; -        use crate::mem::MaybeUninit;-        use crate::pin::{Pin, pin};+        use crate::mem::{DropGuard, MaybeUninit};+        use crate::pin::pin;         use crate::sys::helpers::COpaque;         use crate::sys::{self, cvt_nz, on_broken_pipe_used}; @@ -679,68 +671,52 @@ impl Command {          let pgroup = self.get_pgroup(); -        struct PosixSpawnFileActions<'a>(Pin<&'a COpaque<libc::posix_spawn_file_actions_t>>);--        impl Drop for PosixSpawnFileActions<'_> {-            fn drop(&mut self) {-                unsafe {-                    libc::posix_spawn_file_actions_destroy(self.0.get());-                }-            }-        }--        struct PosixSpawnattr<'a>(Pin<&'a COpaque<libc::posix_spawnattr_t>>);--        impl Drop for PosixSpawnattr<'_> {-            fn drop(&mut self) {-                unsafe {-                    libc::posix_spawnattr_destroy(self.0.get());-                }-            }-        }-         unsafe {             let attrs = pin!(COpaque::uninit());             // FIXME(pin-ergonomics): remove the next line.             let attrs = attrs.into_ref();             cvt_nz(libc::posix_spawnattr_init(attrs.get()))?;-            let attrs = PosixSpawnattr(attrs);+            let attrs = DropGuard::new(attrs, |attrs| {+                libc::posix_spawnattr_destroy(attrs.get());+            });              let mut flags = 0;              let file_actions = pin!(COpaque::uninit());             let file_actions = file_actions.into_ref();             cvt_nz(libc::posix_spawn_file_actions_init(file_actions.get()))?;-            let file_actions = PosixSpawnFileActions(file_actions);+            let file_actions = DropGuard::new(file_actions, |file_actions| {+                libc::posix_spawn_file_actions_destroy(file_actions.get());+            });              if let Some(fd) = stdio.stdin.fd() {                 cvt_nz(libc::posix_spawn_file_actions_adddup2(-                    file_actions.0.get(),+                    file_actions.get(),                     fd,                     libc::STDIN_FILENO,                 ))?;             }             if let Some(fd) = stdio.stdout.fd() {                 cvt_nz(libc::posix_spawn_file_actions_adddup2(-                    file_actions.0.get(),+                    file_actions.get(),                     fd,                     libc::STDOUT_FILENO,                 ))?;             }             if let Some(fd) = stdio.stderr.fd() {                 cvt_nz(libc::posix_spawn_file_actions_adddup2(-                    file_actions.0.get(),+                    file_actions.get(),                     fd,                     libc::STDERR_FILENO,                 ))?;             }             if let Some((f, cwd)) = addchdir {-                cvt_nz(f(file_actions.0.get(), cwd.as_ptr()))?;+                cvt_nz(f(file_actions.get(), cwd.as_ptr()))?;             }              if let Some(pgroup) = pgroup {                 flags |= libc::POSIX_SPAWN_SETPGROUP;-                cvt_nz(libc::posix_spawnattr_setpgroup(attrs.0.get(), pgroup))?;+                cvt_nz(libc::posix_spawnattr_setpgroup(attrs.get(), pgroup))?;             }              // Inherit the signal mask from this process rather than resetting it (i.e. do not call@@ -758,7 +734,7 @@ impl Command {                 {                     cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?;                 }-                cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.get(), default_set.as_ptr()))?;+                cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.get(), default_set.as_ptr()))?;                 flags |= libc::POSIX_SPAWN_SETSIGDEF;             } @@ -773,7 +749,7 @@ impl Command {                 }             } -            cvt_nz(libc::posix_spawnattr_setflags(attrs.0.get(), flags as _))?;+            cvt_nz(libc::posix_spawnattr_setflags(attrs.get(), flags as _))?;              // Make sure we synchronize access to the global `environ` resource             let _env_lock = sys::env::env_read_lock();@@ -790,8 +766,8 @@ impl Command {                 let spawn_res = pidfd_spawnp.get().unwrap()(                     &mut pidfd,                     self.get_program_cstr().as_ptr(),-                    file_actions.0.get(),-                    attrs.0.get(),+                    file_actions.get(),+                    attrs.get(),                     self.get_argv().as_ptr() as *const _,                     envp as *const _,                 );@@ -832,8 +808,8 @@ impl Command {             let spawn_res = spawn_fn(                 &mut p.pid,                 self.get_program_cstr().as_ptr(),-                file_actions.0.get(),-                attrs.0.get(),+                file_actions.get(),+                attrs.get(),                 self.get_argv().as_ptr() as *const _,                 envp as *const _,             );
library/std/src/sys/process/windows/tests.rs4 + / 8
@@ -1,6 +1,7 @@ use super::child_pipe::{Pipes, child_pipe}; use super::{Arg, make_command_line}; use crate::ffi::{OsStr, OsString};+use crate::mem::DropGuard; use crate::os::windows::io::AsHandle; use crate::process::{Command, Stdio}; use crate::time::Duration;@@ -36,14 +37,9 @@ fn test_thread_handle() {     assert!(p.is_ok());      // Ensure the process is killed in the event something goes wrong.-    struct DropGuard(crate::process::Child);-    impl Drop for DropGuard {-        fn drop(&mut self) {-            let _ = self.0.kill();-        }-    }-    let mut p = DropGuard(p.unwrap());-    let p = &mut p.0;+    let mut p = DropGuard::new(p.unwrap(), |mut p| {+        let _: Result<(), crate::io::Error> = p.kill();+    });      unsafe extern "system" {         unsafe fn ResumeThread(hHandle: BorrowedHandle<'_>) -> u32;