Skip to content

Commit 936801f

Browse files
committed
Stop using into_iter in array::map
1 parent e8d4ea2 commit 936801f

File tree

3 files changed

+99
-11
lines changed

3 files changed

+99
-11
lines changed

core/src/array/drain.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use crate::iter::TrustedLen;
2+
use crate::mem::ManuallyDrop;
3+
use crate::ptr::drop_in_place;
4+
use crate::slice;
5+
6+
// INVARIANT: It's ok to drop the remainder of the inner iterator.
7+
pub(crate) struct Drain<'a, T>(slice::IterMut<'a, T>);
8+
9+
pub(crate) fn drain_array_with<T, R, const N: usize>(
10+
array: [T; N],
11+
func: impl for<'a> FnOnce(Drain<'a, T>) -> R,
12+
) -> R {
13+
let mut array = ManuallyDrop::new(array);
14+
// SAFETY: Now that the local won't drop it, it's ok to construct the `Drain` which will.
15+
let drain = Drain(array.iter_mut());
16+
func(drain)
17+
}
18+
19+
impl<T> Drop for Drain<'_, T> {
20+
fn drop(&mut self) {
21+
// SAFETY: By the type invariant, we're allowed to drop all these.
22+
unsafe { drop_in_place(self.0.as_mut_slice()) }
23+
}
24+
}
25+
26+
impl<T> Iterator for Drain<'_, T> {
27+
type Item = T;
28+
29+
#[inline]
30+
fn next(&mut self) -> Option<T> {
31+
let p: *const T = self.0.next()?;
32+
// SAFETY: The iterator was already advanced, so we won't drop this later.
33+
Some(unsafe { p.read() })
34+
}
35+
36+
#[inline]
37+
fn size_hint(&self) -> (usize, Option<usize>) {
38+
let n = self.len();
39+
(n, Some(n))
40+
}
41+
}
42+
43+
impl<T> ExactSizeIterator for Drain<'_, T> {
44+
#[inline]
45+
fn len(&self) -> usize {
46+
self.0.len()
47+
}
48+
}
49+
50+
// SAFETY: This is a 1:1 wrapper for a slice iterator, which is also `TrustedLen`.
51+
unsafe impl<T> TrustedLen for Drain<'_, T> {}

core/src/array/mod.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ use crate::ops::{
1717
};
1818
use crate::slice::{Iter, IterMut};
1919

20+
mod drain;
2021
mod equality;
2122
mod iter;
2223

24+
pub(crate) use drain::drain_array_with;
25+
2326
#[stable(feature = "array_value_iter", since = "1.51.0")]
2427
pub use iter::IntoIter;
2528

@@ -513,9 +516,12 @@ impl<T, const N: usize> [T; N] {
513516
where
514517
F: FnMut(T) -> U,
515518
{
516-
// SAFETY: we know for certain that this iterator will yield exactly `N`
517-
// items.
518-
unsafe { collect_into_array_unchecked(&mut IntoIterator::into_iter(self).map(f)) }
519+
drain_array_with(self, |iter| {
520+
let mut iter = iter.map(f);
521+
// SAFETY: we know for certain that this iterator will yield exactly `N`
522+
// items.
523+
unsafe { collect_into_array_unchecked(&mut iter) }
524+
})
519525
}
520526

521527
/// A fallible function `f` applied to each element on array `self` in order to
@@ -552,9 +558,12 @@ impl<T, const N: usize> [T; N] {
552558
R: Try,
553559
R::Residual: Residual<[R::Output; N]>,
554560
{
555-
// SAFETY: we know for certain that this iterator will yield exactly `N`
556-
// items.
557-
unsafe { try_collect_into_array_unchecked(&mut IntoIterator::into_iter(self).map(f)) }
561+
drain_array_with(self, |iter| {
562+
let mut iter = iter.map(f);
563+
// SAFETY: we know for certain that this iterator will yield exactly `N`
564+
// items.
565+
unsafe { try_collect_into_array_unchecked(&mut iter) }
566+
})
558567
}
559568

560569
/// 'Zips up' two arrays into a single array of pairs.
@@ -575,11 +584,14 @@ impl<T, const N: usize> [T; N] {
575584
/// ```
576585
#[unstable(feature = "array_zip", issue = "80094")]
577586
pub fn zip<U>(self, rhs: [U; N]) -> [(T, U); N] {
578-
let mut iter = IntoIterator::into_iter(self).zip(rhs);
579-
580-
// SAFETY: we know for certain that this iterator will yield exactly `N`
581-
// items.
582-
unsafe { collect_into_array_unchecked(&mut iter) }
587+
drain_array_with(self, |lhs| {
588+
drain_array_with(rhs, |rhs| {
589+
let mut iter = crate::iter::zip(lhs, rhs);
590+
// SAFETY: we know for certain that this iterator will yield exactly `N`
591+
// items.
592+
unsafe { collect_into_array_unchecked(&mut iter) }
593+
})
594+
})
583595
}
584596

585597
/// Returns a slice containing the entire array. Equivalent to `&s[..]`.

core/tests/array.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,3 +700,28 @@ fn array_into_iter_rfold() {
700700
let s = it.rfold(10, |a, b| 10 * a + b);
701701
assert_eq!(s, 10432);
702702
}
703+
704+
#[cfg(not(panic = "abort"))]
705+
#[test]
706+
fn array_map_drops_unmapped_elements_on_panic() {
707+
struct DropCounter<'a>(usize, &'a AtomicUsize);
708+
impl Drop for DropCounter<'_> {
709+
fn drop(&mut self) {
710+
self.1.fetch_add(1, Ordering::SeqCst);
711+
}
712+
}
713+
714+
const MAX: usize = 11;
715+
for panic_after in 0..MAX {
716+
let counter = AtomicUsize::new(0);
717+
let a = array::from_fn::<_, 11, _>(|i| DropCounter(i, &counter));
718+
let success = std::panic::catch_unwind(|| {
719+
let _ = a.map(|x| {
720+
assert!(x.0 < panic_after);
721+
assert_eq!(counter.load(Ordering::SeqCst), x.0);
722+
});
723+
});
724+
assert!(success.is_err());
725+
assert_eq!(counter.load(Ordering::SeqCst), MAX);
726+
}
727+
}

0 commit comments

Comments
 (0)