diff options
| author | The 8472 <git@infinite-source.de> | 2024-06-23 22:16:22 +0200 |
|---|---|---|
| committer | The 8472 <git@infinite-source.de> | 2024-06-25 23:19:58 +0200 |
| commit | 133e7b10a45bddd4ef5ba265d848ce0a1976e8ae (patch) | |
| tree | 43dc1159b870ac66a5ac0d9a0122c6f3cb799648 /library/core | |
| parent | c290e9de32e8ba6a673ef125fde40eadd395d170 (diff) | |
| download | rust-133e7b10a45bddd4ef5ba265d848ce0a1976e8ae.tar.gz rust-133e7b10a45bddd4ef5ba265d848ce0a1976e8ae.zip | |
fix Drop items getting leaked in Filter::next_chunk
The optimization only makes sense for non-drop elements anyway. Use the default implementation for items that are Drop instead. It also simplifies the implementation.
Diffstat (limited to 'library/core')
| -rw-r--r-- | library/core/src/iter/adapters/filter.rs | 88 |
1 files changed, 43 insertions, 45 deletions
diff --git a/library/core/src/iter/adapters/filter.rs b/library/core/src/iter/adapters/filter.rs index ca23d1b13a8..1c99f3938e2 100644 --- a/library/core/src/iter/adapters/filter.rs +++ b/library/core/src/iter/adapters/filter.rs @@ -3,7 +3,7 @@ use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedF use crate::num::NonZero; use crate::ops::Try; use core::array; -use core::mem::{ManuallyDrop, MaybeUninit}; +use core::mem::MaybeUninit; use core::ops::ControlFlow; /// An iterator that filters the elements of `iter` with `predicate`. @@ -27,6 +27,41 @@ impl<I, P> Filter<I, P> { } } +impl<I, P> Filter<I, P> +where + I: Iterator, + P: FnMut(&I::Item) -> bool, +{ + #[inline] + fn next_chunk_dropless<const N: usize>( + &mut self, + ) -> Result<[I::Item; N], array::IntoIter<I::Item, N>> { + let mut array: [MaybeUninit<I::Item>; N] = [const { MaybeUninit::uninit() }; N]; + let mut initialized = 0; + + let result = self.iter.try_for_each(|element| { + let idx = initialized; + initialized = idx + (self.predicate)(&element) as usize; + + // SAFETY: Loop conditions ensure the index is in bounds. + unsafe { array.get_unchecked_mut(idx) }.write(element); + + if initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) } + }); + + match result { + ControlFlow::Break(()) => { + // SAFETY: The loop above is only explicitly broken when the array has been fully initialized + Ok(unsafe { MaybeUninit::array_assume_init(array) }) + } + ControlFlow::Continue(()) => { + // SAFETY: The range is in bounds since the loop breaks when reaching N elements. + Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) }) + } + } + } +} + #[stable(feature = "core_impl_debug", since = "1.9.0")] impl<I: fmt::Debug, P> fmt::Debug for Filter<I, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -64,52 +99,15 @@ where fn next_chunk<const N: usize>( &mut self, ) -> Result<[Self::Item; N], array::IntoIter<Self::Item, N>> { - let mut array: [MaybeUninit<Self::Item>; N] = [const { MaybeUninit::uninit() }; N]; - - struct Guard<'a, T> { - array: &'a mut [MaybeUninit<T>], - initialized: usize, - } - - impl<T> Drop for Guard<'_, T> { - #[inline] - fn drop(&mut self) { - if const { crate::mem::needs_drop::<T>() } { - // SAFETY: self.initialized is always <= N, which also is the length of the array. - unsafe { - core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.array.get_unchecked_mut(..self.initialized), - )); - } - } + let fun = const { + if crate::mem::needs_drop::<I::Item>() { + array::iter_next_chunk::<I::Item, N> + } else { + Self::next_chunk_dropless::<N> } - } - - let mut guard = Guard { array: &mut array, initialized: 0 }; - - let result = self.iter.try_for_each(|element| { - let idx = guard.initialized; - guard.initialized = idx + (self.predicate)(&element) as usize; - - // SAFETY: Loop conditions ensure the index is in bounds. - unsafe { guard.array.get_unchecked_mut(idx) }.write(element); - - if guard.initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) } - }); + }; - let guard = ManuallyDrop::new(guard); - - match result { - ControlFlow::Break(()) => { - // SAFETY: The loop above is only explicitly broken when the array has been fully initialized - Ok(unsafe { MaybeUninit::array_assume_init(array) }) - } - ControlFlow::Continue(()) => { - let initialized = guard.initialized; - // SAFETY: The range is in bounds since the loop breaks when reaching N elements. - Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) }) - } - } + fun(self) } #[inline] |
