diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 1e60107a1d15c..98904aa500669 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -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 WithHeader { // - Assumes that either `value` can be dereferenced, or is the // `NonNull::dangling()` we use when both `T` and `H` are ZSTs. unsafe fn drop(&self, value: *mut T) { - struct DropGuard { - ptr: NonNull, - value_layout: Layout, - _marker: PhantomData, - } - - impl Drop for DropGuard { - 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::::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::::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::, - }; - // 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. diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index cf6018f917a54..0fc83871c815f 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -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); } } } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index e8832fd6e27ca..da08f9bfa36ed 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -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 IntoIterator for BTreeMap { #[stable(feature = "btree_drop", since = "1.7.0")] impl Drop for IntoIter { fn drop(&mut self) { - struct DropGuard<'a, K, V, A: AllocatorClone>(&'a mut IntoIter); - - 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); } } } diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index ad86e9422d974..9734649fd5adc 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -16,13 +16,7 @@ pub(super) fn take_mut(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(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); @@ -30,6 +24,6 @@ pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { unsafe { ptr::write(v, new_value); } - mem::forget(guard); + mem::DropGuard::dismiss(guard); ret } diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index aa38d17bb6dbc..c97f7ac00474a 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -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 Handle, 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); - impl 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 } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 1417f56e46cf9..953cffc1c396d 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -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 LinkedList { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for LinkedList { fn drop(&mut self) { - struct DropGuard<'a, T, A: Allocator>(&'a mut LinkedList); - - 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); } } diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index b56af5f0e85b6..48955361e7675 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -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 Send for Drain<'_, T, A> {} #[stable(feature = "drain", since = "1.6.0")] impl 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::() && 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::() && 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::() && 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( - source_deque: &mut VecDeque, - 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( + source_deque: &mut VecDeque, + 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::() && 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) }; } } } diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index e18b85dd4b694..7c83fff6c4ab1 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -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 Iterator for IntoIter { F: FnMut(B, Self::Item) -> R, R: Try, { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - // `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 Iterator for IntoIter { tail.iter() .map(|elem| { - guard.consumed += 1; + *consumed += 1; // SAFETY: Same as above. unsafe { ptr::read(elem) } }) @@ -201,26 +193,18 @@ impl DoubleEndedIterator for IntoIter { F: FnMut(B, Self::Item) -> R, R: Try, { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - // `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) } }) @@ -228,7 +212,7 @@ impl DoubleEndedIterator for IntoIter { head.iter() .map(|elem| { - guard.consumed += 1; + *consumed += 1; // SAFETY: Same as above. unsafe { ptr::read(elem) } }) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 385e172b23207..08abf0e5c5a68 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -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 VecDeque { mut iter: impl Iterator, len: usize, ) -> usize { - struct Guard<'a, T, A: Allocator> { - deque: &'a mut VecDeque, - 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 diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 09540183c488f..b4822d98bb45a 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2450,47 +2450,32 @@ impl Rc<[T]> { /// Behavior is undefined should the size be wrong. #[cfg(not(no_global_oom_handling))] unsafe fn from_iter_exact(iter: impl Iterator, 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 { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - 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) } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index c950569e9838b..541b3413f71ba 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -408,35 +408,30 @@ impl [T] { impl ConvertVec for T { #[inline] default fn to_vec(s: &[Self], alloc: A) -> Vec { - struct DropGuard<'a, T, A: Allocator> { - vec: &'a mut Vec, - 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 } } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 38c36fa25e41e..d2b5a5a53a34a 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -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`. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 5754e48a41e0a..27192cf686c12 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -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 Arc<[T]> { /// Behavior is undefined should the size be wrong. #[cfg(not(no_global_oom_handling))] unsafe fn from_iter_exact(iter: impl Iterator, 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 { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - 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 Arc { // 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, - } - 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 Arc { ); // 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. diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs index df3ff0a9b769f..8dae87b480256 100644 --- a/library/alloc/src/vec/drain.rs +++ b/library/alloc/src/vec/drain.rs @@ -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 DoubleEndedIterator for Drain<'_, T, A> { #[stable(feature = "drain", since = "1.6.0")] impl 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 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; diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 46874ff76c093..fd19585a680bb 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -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 Clone for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { fn drop(&mut self) { - struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter); - - impl 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 } } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 74b4322d027c5..1045a7b7e2f56 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2299,19 +2299,6 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { #[cfg(target_vendor = "apple")] pub fn copy(from: &Path, to: &Path) -> io::Result { 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 { // 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, ) diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index 7c9dcdc8b7375..e3294a0051d34 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -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>); - 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::::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); } } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index aa47fcb3360c1..ba04471631be9 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -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>); - - impl Drop for PosixSpawnFileActions<'_> { - fn drop(&mut self) { - unsafe { - libc::posix_spawn_file_actions_destroy(self.0.get()); - } - } - } - - struct PosixSpawnattr<'a>(Pin<&'a COpaque>); - - 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 _, ); diff --git a/library/std/src/sys/process/windows/tests.rs b/library/std/src/sys/process/windows/tests.rs index bc5e0d5c7fc97..4d13f4d9e3b9f 100644 --- a/library/std/src/sys/process/windows/tests.rs +++ b/library/std/src/sys/process/windows/tests.rs @@ -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;