From 6b23f12127cd221e92485184ddf588babaee58f5 Mon Sep 17 00:00:00 2001 From: TusharB-07 Date: Sat, 7 Feb 2026 00:11:10 +0530 Subject: [PATCH 1/2] Fix: Drop ZST elements properly in array::map and array::try_map Fixes #152211 Previously, the Drain::drop implementation would skip dropping zero-sized types entirely, causing Drop implementations on ZSTs to never run when a closure panicked or exited early. This fix adds a zst_processed counter to track how many ZST elements have been processed, ensuring that remaining unprocessed ZSTs are properly dropped when the Drain is dropped. For ZSTs, conjure_zst() creates new values rather than moving from the array, so all N original elements remain and must be accounted for. --- library/core/src/array/drain.rs | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 1c6137191324c..42d2fb277fce4 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -25,7 +25,7 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { // for direct pointer equality with `ptr` to check if the drainer is done. unsafe { let end = if T::IS_ZST { null_mut() } else { ptr.as_ptr().add(N) }; - Self { ptr, end, f, l: PhantomData } + Self { ptr, end, zst_processed: 0, f, l: PhantomData } } } } @@ -44,6 +44,8 @@ pub(super) struct Drain<'l, 'f, T, const N: usize, F> { /// For non-ZSTs, the non-null pointer to the past-the-end element. /// For ZSTs, this is null. end: *mut T, + /// For ZSTs, tracks how many elements have been processed + zst_processed: usize, f: &'f mut F, l: PhantomData<&'l mut [T; N]>, @@ -74,6 +76,8 @@ where (_ /* ignore argument */,): (usize,), ) -> Self::Output { if T::IS_ZST { + // Track that we've processed one more ZST + self.zst_processed += 1; // its UB to call this more than N times, so returning more ZSTs is valid. // SAFETY: its a ZST? we conjur. (self.f)(unsafe { conjure_zst::() }) @@ -93,16 +97,28 @@ impl const Drop for Drain<'_, '_, T, N, fn drop(&mut self) { if !T::IS_ZST { // SAFETY: we cant read more than N elements - let slice = unsafe { - from_raw_parts_mut::<[T]>( - self.ptr.as_ptr(), - // SAFETY: `start <= end` - self.end.offset_from_unsigned(self.ptr.as_ptr()), - ) - }; + let slice = from_raw_parts_mut::<[T]>( + self.ptr.as_ptr(), + // SAFETY: `start <= end` + unsafe { self.end.offset_from_unsigned(self.ptr.as_ptr()) }, + ); // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) unsafe { drop_in_place(slice) } + } else { + // For ZSTs, drop the remaining unprocessed elements + // conjure_zst creates new values, so the original N elements are still in the array + let remaining = N - self.zst_processed; + let mut i = 0; + while i < remaining { + // SAFETY: By the type invariant, we're allowed to drop the remaining ZST elements + unsafe { drop_in_place(self.ptr.as_ptr()) } + i += 1; + } } } } + + + + From 9d80af8c89bddc236625ec5fa3cf72adadbd8ccf Mon Sep 17 00:00:00 2001 From: TusharB-07 Date: Sun, 8 Feb 2026 19:56:37 +0530 Subject: [PATCH 2/2] Fix formatting: remove extra blank lines --- library/core/src/array/drain.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 42d2fb277fce4..8c28f53c054dd 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -118,7 +118,3 @@ impl const Drop for Drain<'_, '_, T, N, } } } - - - -