@@ -3,7 +3,7 @@ use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedF
33use crate::num::NonZero;
44use crate::ops::Try;
55use core::array;
6- use core::mem::{ManuallyDrop, MaybeUninit} ;
6+ use core::mem::MaybeUninit;
77use core::ops::ControlFlow;
88
99/// An iterator that filters the elements of `iter` with `predicate`.
@@ -27,6 +27,42 @@ impl<I, P> Filter<I, P> {
2727 }
2828}
2929
30+ impl<I, P> Filter<I, P>
31+ where
32+ I: Iterator,
33+ P: FnMut(&I::Item) -> bool,
34+ {
35+ #[inline]
36+ fn next_chunk_dropless<const N: usize>(
37+ &mut self,
38+ ) -> Result<[I::Item; N], array::IntoIter<I::Item, N>> {
39+ let mut array: [MaybeUninit<I::Item>; N] = [const { MaybeUninit::uninit() }; N];
40+ let mut initialized = 0;
41+
42+ let result = self.iter.try_for_each(|element| {
43+ let idx = initialized;
44+ // branchless index update combined with unconditionally copying the value even when
45+ // it is filtered reduces branching and dependencies in the loop.
46+ initialized = idx + (self.predicate)(&element) as usize;
47+ // SAFETY: Loop conditions ensure the index is in bounds.
48+ unsafe { array.get_unchecked_mut(idx) }.write(element);
49+
50+ if initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
51+ });
52+
53+ match result {
54+ ControlFlow::Break(()) => {
55+ // SAFETY: The loop above is only explicitly broken when the array has been fully initialized
56+ Ok(unsafe { MaybeUninit::array_assume_init(array) })
57+ }
58+ ControlFlow::Continue(()) => {
59+ // SAFETY: The range is in bounds since the loop breaks when reaching N elements.
60+ Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) })
61+ }
62+ }
63+ }
64+ }
65+
3066#[stable(feature = "core_impl_debug", since = "1.9.0")]
3167impl<I: fmt::Debug, P> fmt::Debug for Filter<I, P> {
3268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -64,52 +100,16 @@ where
64100 fn next_chunk<const N: usize>(
65101 &mut self,
66102 ) -> Result<[Self::Item; N], array::IntoIter<Self::Item, N>> {
67- let mut array: [MaybeUninit<Self::Item>; N] = [const { MaybeUninit::uninit() }; N];
68-
69- struct Guard<'a, T> {
70- array: &'a mut [MaybeUninit<T>],
71- initialized: usize,
72- }
73-
74- impl<T> Drop for Guard<'_, T> {
75- #[inline]
76- fn drop(&mut self) {
77- if const { crate::mem::needs_drop::<T>() } {
78- // SAFETY: self.initialized is always <= N, which also is the length of the array.
79- unsafe {
80- core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(
81- self.array.get_unchecked_mut(..self.initialized),
82- ));
83- }
84- }
103+ // avoid codegen for the dead branch
104+ let fun = const {
105+ if crate::mem::needs_drop::<I::Item>() {
106+ array::iter_next_chunk::<I::Item, N>
107+ } else {
108+ Self::next_chunk_dropless::<N>
85109 }
86- }
87-
88- let mut guard = Guard { array: &mut array, initialized: 0 };
89-
90- let result = self.iter.try_for_each(|element| {
91- let idx = guard.initialized;
92- guard.initialized = idx + (self.predicate)(&element) as usize;
93-
94- // SAFETY: Loop conditions ensure the index is in bounds.
95- unsafe { guard.array.get_unchecked_mut(idx) }.write(element);
96-
97- if guard.initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
98- });
110+ };
99111
100- let guard = ManuallyDrop::new(guard);
101-
102- match result {
103- ControlFlow::Break(()) => {
104- // SAFETY: The loop above is only explicitly broken when the array has been fully initialized
105- Ok(unsafe { MaybeUninit::array_assume_init(array) })
106- }
107- ControlFlow::Continue(()) => {
108- let initialized = guard.initialized;
109- // SAFETY: The range is in bounds since the loop breaks when reaching N elements.
110- Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) })
111- }
112- }
112+ fun(self)
113113 }
114114
115115 #[inline]
0 commit comments