diff --git a/library/core/benches/iter.rs b/library/core/benches/iter.rs index 24257ba98785d..aed3b2a2be9d7 100644 --- a/library/core/benches/iter.rs +++ b/library/core/benches/iter.rs @@ -367,3 +367,53 @@ fn bench_partial_cmp(b: &mut Bencher) { fn bench_lt(b: &mut Bencher) { b.iter(|| (0..100000).map(black_box).lt((0..100000).map(black_box))) } + +#[bench] +fn bench_iter_array_map(b: &mut Bencher) { + b.iter(|| { + let mut acc = 0; + let iter = (0i64..10000).map(black_box).map(|_| black_box([0i64; 100])); + for_each_fold(iter, |x: [i64; 100]| acc += x.iter().sum::()); + acc + }); +} + +#[bench] +fn bench_iter_array_chunks_loop(b: &mut Bencher) { + b.iter(|| { + let mut acc = 0; + let iter = (0i64..1000000).array_chunks::<100>().map(black_box); + for_each_loop(iter, |x: [i64; 100]| acc += x.iter().sum::()); + acc + }); +} + +#[bench] +fn bench_iter_array_chunks_fold(b: &mut Bencher) { + b.iter(|| { + let mut acc = 0; + let iter = (0i64..1000000).array_chunks::<100>().map(black_box); + for_each_fold(iter, |x: [i64; 100]| acc += x.iter().sum::()); + acc + }); +} + +#[bench] +fn bench_iter_array_chunks_rev_loop(b: &mut Bencher) { + b.iter(|| { + let mut acc = 0; + let iter = (0i64..1000000).array_chunks::<100>().map(black_box); + for_each_loop(iter.rev(), |x: [i64; 100]| acc += x.iter().sum::()); + acc + }); +} + +#[bench] +fn bench_iter_array_chunks_rfold(b: &mut Bencher) { + b.iter(|| { + let mut acc = 0; + let iter = (0i64..1000000).array_chunks::<100>().map(black_box); + for_each_fold(iter.rev(), |x: [i64; 100]| acc += x.iter().sum::()); + acc + }); +} diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs index de4ef7949f344..5dbb9e559521c 100644 --- a/library/core/benches/lib.rs +++ b/library/core/benches/lib.rs @@ -1,6 +1,8 @@ // wasm32 does not support benches (no time). #![cfg(not(target_arch = "wasm32"))] +#![feature(array_chunks)] #![feature(flt2dec)] +#![feature(iter_array_chunks)] #![feature(test)] extern crate test; diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 3bc9f71375cb8..f86c6a7f1786a 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -437,6 +437,38 @@ impl [T; N] { } } +pub(crate) struct Guard { + ptr: *mut T, + pub(crate) init: usize, +} + +impl Drop for Guard { + fn drop(&mut self) { + debug_assert!(self.init <= N); + + let init_part = crate::ptr::slice_from_raw_parts_mut(self.ptr, self.init); + + // SAFETY: this raw slice will contain only initialized objects. + unsafe { + crate::ptr::drop_in_place(init_part); + } + } +} + +impl Guard { + /// Creates a `Guard` object that will drop the initialized portion + /// of the provided array when dropped. + /// + /// # Safety + /// + /// When the returned guard is dropped, the array must still be valid, + /// `init` must not be greater `N`, and the first `init` elements of + /// the array must be properly initialized. + pub(crate) unsafe fn new(array: &mut [MaybeUninit; N]) -> Self { + Self { ptr: MaybeUninit::slice_as_mut_ptr(array), init: 0 } + } +} + /// Pulls `N` items from `iter` and returns them as an array. If the iterator /// yields fewer than `N` items, this function exhibits undefined behavior. /// @@ -484,39 +516,23 @@ where return unsafe { Some(mem::zeroed()) }; } - struct Guard { - ptr: *mut T, - initialized: usize, - } - - impl Drop for Guard { - fn drop(&mut self) { - debug_assert!(self.initialized <= N); - - let initialized_part = crate::ptr::slice_from_raw_parts_mut(self.ptr, self.initialized); - - // SAFETY: this raw slice will contain only initialized objects. - unsafe { - crate::ptr::drop_in_place(initialized_part); - } - } - } - let mut array = MaybeUninit::uninit_array::(); - let mut guard: Guard<_, N> = - Guard { ptr: MaybeUninit::slice_as_mut_ptr(&mut array), initialized: 0 }; + // SAFETY: `guard` is always either forgotten or dropped before the array + // is moved/dropped and `guard.init` properly tracks the initialized + // members of the array. + let mut guard = unsafe { Guard::new(&mut array) }; while let Some(item) = iter.next() { - // SAFETY: `guard.initialized` starts at 0, is increased by one in the + // SAFETY: `guard.init` starts at 0, is increased by one in the // loop and the loop is aborted once it reaches N (which is // `array.len()`). unsafe { - array.get_unchecked_mut(guard.initialized).write(item); + array.get_unchecked_mut(guard.init).write(item); } - guard.initialized += 1; + guard.init += 1; // Check if the whole array was initialized. - if guard.initialized == N { + if guard.init == N { mem::forget(guard); // SAFETY: the condition above asserts that all elements are @@ -527,7 +543,7 @@ where } // This is only reached if the iterator is exhausted before - // `guard.initialized` reaches `N`. Also note that `guard` is dropped here, + // `guard.init` reaches `N`. Also note that `guard` is dropped here, // dropping all already initialized elements. None } diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs new file mode 100644 index 0000000000000..d5974b9c60c73 --- /dev/null +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -0,0 +1,340 @@ +use core::array::Guard; +use core::iter::FusedIterator; +use core::mem::{self, MaybeUninit}; +use core::ops::{ControlFlow, Try}; +use core::ptr; + +#[derive(Debug)] +struct Buffer { + array: [MaybeUninit; N], + init: usize, +} + +impl Buffer { + fn new() -> Self { + Self { array: MaybeUninit::uninit_array(), init: 0 } + } +} + +impl Clone for Buffer { + fn clone(&self) -> Self { + let mut new = Self::new(); + // SAFETY: this raw slice contains only the initialized objects. + let src = unsafe { MaybeUninit::slice_assume_init_ref(&self.array[..self.init]) }; + MaybeUninit::write_slice_cloned(&mut new.array[..self.init], src); + new.init = self.init; + new + } +} + +impl Drop for Buffer { + fn drop(&mut self) { + debug_assert!(self.init <= N); + + let initialized_part = &mut self.array[..self.init]; + + // SAFETY: this raw slice will contain only the initialized objects + // that have not been dropped or moved. + unsafe { + ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); + } + } +} + +/// An iterator that yields the elements of another iterator in +/// chunks of size `N`. +/// +/// This `struct` is created by the [`array_chunks`] method on [`Iterator`]. See +/// its documentation for more. +/// +/// [`array_chunks`]: Iterator::array_chunks +#[unstable(feature = "iter_array_chunks", issue = "none")] +#[derive(Debug, Clone)] +pub struct ArrayChunks { + iter: I, + buffer: Buffer, + done: bool, +} + +impl ArrayChunks { + pub(in crate::iter) fn new(iter: I) -> Self { + Self { iter, buffer: Buffer::new(), done: false } + } + + /// Returns the remainder of the elements yielded by the original + /// iterator that were insufficient to fill another chunk. The + /// returned slice has at most `N-1` elements. + #[unstable(feature = "iter_array_chunks", issue = "none")] + pub fn remainder(&self) -> &[I::Item] { + // SAFETY: We know that all elements before `init` are properly initialized. + unsafe { MaybeUninit::slice_assume_init_ref(&self.buffer.array[..self.buffer.init]) } + } + + /// Returns the remainder of the elements yielded by the original + /// iterator that were insufficient to fill another chunk. The + /// returned slice has at most `N-1` elements. + #[unstable(feature = "iter_array_chunks", issue = "none")] + pub fn remainder_mut(&mut self) -> &mut [I::Item] { + // SAFETY: We know that all elements before `init` are properly initialized. + unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buffer.array[..self.buffer.init]) } + } +} + +#[unstable(feature = "iter_array_chunks", issue = "none")] +impl Iterator for ArrayChunks { + type Item = [I::Item; N]; + + fn next(&mut self) -> Option { + if self.done { + return None; + } + + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `guard` is always either forgotten or dropped before the array + // is moved/dropped and `guard.init` properly tracks the initialized + // members of the array. + let mut guard = unsafe { Guard::new(&mut array) }; + for slot in &mut array { + let next = match self.iter.next() { + Some(n) => n, + None => { + self.done = true; + if guard.init > 0 { + self.buffer.init = guard.init; + mem::forget(guard); + self.buffer.array = array; + } + return None; + } + }; + slot.write(next); + guard.init += 1; + } + mem::forget(guard); + // SAFETY: The entire array has just been initialized. + unsafe { Some(MaybeUninit::array_assume_init(array)) } + } + + fn size_hint(&self) -> (usize, Option) { + if self.done { + return (0, Some(0)); + } + let (lower, upper) = self.iter.size_hint(); + (lower / N, upper.map(|x| x / N)) + } + + fn try_fold(&mut self, acc: Acc, mut fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + if self.done { + return R::from_output(acc); + } + + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `guard` is always either forgotten or dropped before the array + // is moved/dropped and `guard.init` properly tracks the initialized + // members of the array. + let mut guard = unsafe { Guard::new(&mut array) }; + let result = self.iter.try_fold(acc, |mut acc, x| { + // SAFETY: `guard.init` starts at 0, is increased by one each iteration + // until it equals N (which is `array.len()`) and is reset to 0. + unsafe { + array.get_unchecked_mut(guard.init).write(x); + } + guard.init += 1; + + if guard.init == N { + guard.init = 0; + // SAFETY: The entire array has just been initialized. + let array = unsafe { + MaybeUninit::array_assume_init(mem::replace( + &mut array, + MaybeUninit::uninit_array(), + )) + }; + acc = fold(acc, array)?; + } + R::from_output(acc) + }); + match result.branch() { + ControlFlow::Continue(x) => { + self.done = true; + if guard.init > 0 { + self.buffer.init = guard.init; + mem::forget(guard); + self.buffer.array = array; + } + R::from_output(x) + } + ControlFlow::Break(x) => R::from_residual(x), + } + } + + fn fold(self, acc: Acc, mut fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.done { + return acc; + } + + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `guard` is always either forgotten or dropped before the array + // is moved/dropped and `guard.init` properly tracks the initialized + // members of the array. + let mut guard = unsafe { Guard::new(&mut array) }; + self.iter.fold(acc, |mut acc, x| { + // SAFETY: `guard.init` starts at 0, is increased by one each iteration + // until it equals N (which is `array.len()`) and is reset to 0. + unsafe { + array.get_unchecked_mut(guard.init).write(x); + } + guard.init += 1; + + if guard.init == N { + guard.init = 0; + // SAFETY: The entire array has just been initialized. + let array = unsafe { + MaybeUninit::array_assume_init(mem::replace( + &mut array, + MaybeUninit::uninit_array(), + )) + }; + acc = fold(acc, array); + } + acc + }) + } +} + +#[unstable(feature = "iter_array_chunks", issue = "none")] +impl DoubleEndedIterator for ArrayChunks { + fn next_back(&mut self) -> Option { + if self.done { + return None; + } + + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `guard` is always either forgotten or dropped before the array + // is moved/dropped and `guard.init` properly tracks the initialized + // members of the array. + let mut guard = unsafe { Guard::new(&mut array) }; + for slot in &mut array { + let next = match self.iter.next_back() { + Some(n) => n, + None => { + self.done = true; + if guard.init > 0 { + (&mut array[..guard.init]).reverse(); + self.buffer.init = guard.init; + mem::forget(guard); + self.buffer.array = array; + } + return None; + } + }; + slot.write(next); + guard.init += 1; + } + guard.init = 0; + array.reverse(); + // SAFETY: The entire array has just been initialized. + unsafe { Some(MaybeUninit::array_assume_init(array)) } + } + + fn try_rfold(&mut self, acc: Acc, mut fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + if self.done { + return R::from_output(acc); + } + + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `guard` is always either forgotten or dropped before the array + // is moved/dropped and `guard.init` properly tracks the initialized + // members of the array. + let mut guard = unsafe { Guard::new(&mut array) }; + let result = self.iter.try_rfold(acc, |mut acc, x| { + // SAFETY: `guard.init` starts at 0, is increased by one each iteration + // until it equals N (which is `array.len()`) and is reset to 0. + unsafe { + array.get_unchecked_mut(guard.init).write(x); + } + guard.init += 1; + + if guard.init == N { + guard.init = 0; + array.reverse(); + // SAFETY: The entire array has just been initialized. + let array = unsafe { + MaybeUninit::array_assume_init(mem::replace( + &mut array, + MaybeUninit::uninit_array(), + )) + }; + acc = fold(acc, array)?; + } + R::from_output(acc) + }); + match result.branch() { + ControlFlow::Continue(x) => { + self.done = true; + if guard.init > 0 { + (&mut array[..guard.init]).reverse(); + self.buffer.init = guard.init; + mem::forget(guard); + self.buffer.array = array; + } + R::from_output(x) + } + ControlFlow::Break(x) => R::from_residual(x), + } + } + + fn rfold(self, acc: Acc, mut fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.done { + return acc; + } + + let mut array = MaybeUninit::uninit_array(); + // SAFETY: `guard` is always either forgotten or dropped before the array + // is moved/dropped and `guard.init` properly tracks the initialized + // members of the array. + let mut guard = unsafe { Guard::new(&mut array) }; + self.iter.rfold(acc, |mut acc, x| { + // SAFETY: `guard.init` starts at 0, is increased by one each iteration + // until it equals N (which is `array.len()`) and is reset to 0. + unsafe { + array.get_unchecked_mut(guard.init).write(x); + } + guard.init += 1; + + if guard.init == N { + guard.init = 0; + array.reverse(); + // SAFETY: The entire array has just been initialized. + let array = unsafe { + MaybeUninit::array_assume_init(mem::replace( + &mut array, + MaybeUninit::uninit_array(), + )) + }; + acc = fold(acc, array); + } + acc + }) + } +} + +#[unstable(feature = "iter_array_chunks", issue = "none")] +impl ExactSizeIterator for ArrayChunks {} + +#[unstable(feature = "iter_array_chunks", issue = "none")] +impl FusedIterator for ArrayChunks {} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 056ccca1d01c9..d403a2d7a4c2b 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -1,6 +1,7 @@ use crate::iter::{InPlaceIterable, Iterator}; use crate::ops::{ControlFlow, Try}; +mod array_chunks; mod chain; mod cloned; mod copied; @@ -30,6 +31,9 @@ pub use self::{ scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip, }; +#[unstable(feature = "iter_array_chunks", issue = "none")] +pub use self::array_chunks::ArrayChunks; + #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::cloned::Cloned; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 7fb80f954ff40..7232c9fcf303b 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -393,6 +393,8 @@ pub use self::traits::{ #[unstable(feature = "iter_zip", issue = "83574")] pub use self::adapters::zip; +#[unstable(feature = "iter_array_chunks", issue = "none")] +pub use self::adapters::ArrayChunks; #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::adapters::Cloned; #[stable(feature = "iter_copied", since = "1.36.0")] diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 537e42f66de1b..961af58957822 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -5,6 +5,7 @@ use crate::cmp::{self, Ordering}; use crate::ops::{ControlFlow, Try}; +use super::super::ArrayChunks; use super::super::TrustedRandomAccessNoCoerce; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; @@ -3468,6 +3469,36 @@ pub trait Iterator { { unreachable!("Always specialized"); } + + /// Creates an iterator which yields arrays of `N` elements yielded by + /// the original iterator. + /// + /// # Panics + /// + /// Panics if `N` is zero. + /// + /// # Examples + /// + /// ``` + /// #![feature(iter_array_chunks)] + /// let mut iter = (0..10).array_chunks::<3>(); + /// + /// assert_eq!(iter.next(), Some([0, 1, 2])); + /// assert_eq!(iter.next(), Some([3, 4, 5])); + /// assert_eq!(iter.next(), Some([6, 7, 8])); + /// assert_eq!(iter.next(), None); + /// + /// assert_eq!(iter.remainder(), &[9]); + /// ``` + #[inline] + #[unstable(feature = "iter_array_chunks", issue = "none")] + fn array_chunks(self) -> ArrayChunks + where + Self: Sized, + { + assert_ne!(N, 0); + ArrayChunks::new(self) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/core/tests/iter/adapters/array_chunks.rs new file mode 100644 index 0000000000000..77531e4bea9a5 --- /dev/null +++ b/library/core/tests/iter/adapters/array_chunks.rs @@ -0,0 +1,234 @@ +use core::cell::Cell; +use core::iter::*; + +#[derive(Debug, Clone)] +struct DropBomb<'a> { + dropped: bool, + counter: &'a Cell, +} + +impl<'a> DropBomb<'a> { + fn new(counter: &'a Cell) -> Self { + Self { dropped: false, counter } + } +} + +impl Drop for DropBomb<'_> { + fn drop(&mut self) { + if self.dropped { + panic!("double dropped!"); + } + self.dropped = true; + self.counter.set(self.counter.get() + 1); + } +} + +#[test] +fn test_iterator_array_chunks_clone() { + let mut iter = (0..=10).array_chunks::<4>(); + let mut iter2 = iter.clone(); + for (x, y) in iter.by_ref().zip(iter2.by_ref()) { + assert_eq!(x, y); + } + assert_eq!(iter.remainder(), &[8, 9, 10]); + assert_eq!(iter2.remainder(), &[]); + assert_eq!(iter2.next(), None); + assert_eq!(iter2.remainder(), &[8, 9, 10]); + + let counter = Cell::new(0); + let mut iter = once(DropBomb::new(&counter)).cycle().take(11).array_chunks::<3>(); + let mut iter2 = iter.clone(); + for (i, (_x, _y)) in iter.by_ref().zip(iter2.by_ref()).enumerate() { + assert_eq!(counter.get(), i * 6); + } + assert_eq!(counter.get(), 18); + drop(iter); + assert_eq!(counter.get(), 21); + assert_eq!(iter2.remainder().len(), 0); + assert!(iter2.next().is_none()); + assert_eq!(iter2.remainder().len(), 2); + drop(iter2); + assert_eq!(counter.get(), 24); +} + +#[test] +fn test_iterator_array_chunks_remainder() { + let mut iter = (0..=10).array_chunks::<4>(); + assert_eq!(iter.remainder(), &[]); + assert_eq!(iter.remainder_mut(), &[]); + assert_eq!(iter.next(), Some([0, 1, 2, 3])); + assert_eq!(iter.remainder(), &[]); + assert_eq!(iter.remainder_mut(), &[]); + assert_eq!(iter.next(), Some([4, 5, 6, 7])); + assert_eq!(iter.remainder(), &[]); + assert_eq!(iter.remainder_mut(), &[]); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + assert_eq!(iter.remainder(), &[8, 9, 10]); + assert_eq!(iter.remainder_mut(), &[8, 9, 10]); +} + +#[test] +fn test_iterator_array_chunks_drop() { + let counter = Cell::new(0); + let create = |n| (0..n).map(|_| DropBomb::new(&counter)).array_chunks::<3>(); + + let iter = create(5); + assert_eq!(counter.get(), 0); + drop(iter); + assert_eq!(counter.get(), 0); + + let mut iter = create(3); + counter.set(0); + iter.next(); + assert_eq!(counter.get(), 3); + assert!(iter.next().is_none()); + assert_eq!(counter.get(), 3); + assert_eq!(iter.remainder().len(), 0); + drop(iter); + assert_eq!(counter.get(), 3); + + let mut iter = create(5); + counter.set(0); + iter.next(); + assert_eq!(counter.get(), 3); + assert!(iter.next().is_none()); + assert_eq!(counter.get(), 3); + assert_eq!(iter.remainder().len(), 2); + drop(iter); + assert_eq!(counter.get(), 5); +} + +#[test] +fn test_iterator_array_chunks_try_fold() { + let mut iter = (0..=10).array_chunks::<3>(); + let result = iter.try_fold(0, |acc, arr| { + assert_eq!(arr, [acc * 3, (acc * 3) + 1, (acc * 3) + 2]); + if acc == 2 { Err(acc) } else { Ok(acc + 1) } + }); + assert_eq!(result, Err(2)); + assert_eq!(iter.remainder(), &[]); + assert_eq!(iter.next(), None); + assert_eq!(iter.remainder(), &[9, 10]); + + let mut iter = (0..10).array_chunks::<2>(); + let result: Result<_, ()> = iter.try_fold(0, |acc, arr| { + assert_eq!(arr, [acc * 2, (acc * 2) + 1]); + Ok(acc + 1) + }); + assert_eq!(result, Ok(5)); + assert_eq!(iter.next(), None); + assert_eq!(iter.remainder(), &[]); + + let counter = Cell::new(0); + let mut iter = (0..=10).map(|_| DropBomb::new(&counter)).array_chunks::<3>(); + let result = iter.try_fold(0, |acc, _arr| { + assert_eq!(counter.get(), acc * 3); + if acc == 1 { Err(acc) } else { Ok(acc + 1) } + }); + assert_eq!(result, Err(1)); + assert_eq!(iter.remainder().len(), 0); + assert_eq!(counter.get(), 6); + drop(iter); + assert_eq!(counter.get(), 6); + + counter.set(0); + let mut iter = (0..=10).map(|_| DropBomb::new(&counter)).array_chunks::<3>(); + let result: Result<_, ()> = iter.try_fold(0, |acc, _arr| { + assert_eq!(counter.get(), acc * 3); + Ok(acc + 1) + }); + assert_eq!(result, Ok(3)); + assert_eq!(iter.remainder().len(), 2); + assert_eq!(counter.get(), 9); + drop(iter); + assert_eq!(counter.get(), 11); +} + +#[test] +fn test_iterator_array_chunks_fold() { + let result = (0..10).array_chunks::<3>().fold(0, |acc, arr| { + assert_eq!(arr, [acc * 3, (acc * 3) + 1, (acc * 3) + 2]); + acc + 1 + }); + assert_eq!(result, 3); + + let counter = Cell::new(0); + (0..10).map(|_| DropBomb::new(&counter)).array_chunks::<3>().fold(0, |acc, _arr| { + assert_eq!(counter.get(), acc * 3); + acc + 1 + }); + assert_eq!(counter.get(), 10); +} + +#[test] +fn test_iterator_array_chunks_rev() { + let mut iter = (0..=10).array_chunks::<4>(); + assert_eq!(iter.next_back(), Some([7, 8, 9, 10])); + assert_eq!(iter.next_back(), Some([3, 4, 5, 6])); + assert_eq!(iter.next_back(), None); + assert_eq!(iter.next_back(), None); + assert_eq!(iter.remainder(), &[0, 1, 2]); +} + +#[test] +fn test_iterator_array_chunks_try_rfold() { + let mut iter = (0..=10).array_chunks::<3>(); + let result = iter.try_rfold(0, |acc, arr| { + assert_eq!(arr, [8 - (acc * 3), 9 - (acc * 3), 10 - (acc * 3)]); + if acc == 2 { Err(acc) } else { Ok(acc + 1) } + }); + assert_eq!(result, Err(2)); + assert_eq!(iter.remainder(), &[]); + assert_eq!(iter.next(), None); + assert_eq!(iter.remainder(), &[0, 1]); + + let mut iter = (0..10).array_chunks::<2>(); + let result: Result<_, ()> = iter.try_rfold(0, |acc, arr| { + assert_eq!(arr, [8 - (acc * 2), 9 - (acc * 2)]); + Ok(acc + 1) + }); + assert_eq!(result, Ok(5)); + assert_eq!(iter.next(), None); + assert_eq!(iter.remainder(), &[]); + + let counter = Cell::new(0); + let mut iter = (0..=10).map(|_| DropBomb::new(&counter)).array_chunks::<3>(); + let result = iter.try_rfold(0, |acc, _arr| { + assert_eq!(counter.get(), acc * 3); + if acc == 1 { Err(acc) } else { Ok(acc + 1) } + }); + assert_eq!(result, Err(1)); + assert_eq!(iter.remainder().len(), 0); + assert_eq!(counter.get(), 6); + drop(iter); + assert_eq!(counter.get(), 6); + + counter.set(0); + let mut iter = (0..=10).map(|_| DropBomb::new(&counter)).array_chunks::<3>(); + let result: Result<_, ()> = iter.try_rfold(0, |acc, _arr| { + assert_eq!(counter.get(), acc * 3); + Ok(acc + 1) + }); + assert_eq!(result, Ok(3)); + assert_eq!(iter.remainder().len(), 2); + assert_eq!(counter.get(), 9); + drop(iter); + assert_eq!(counter.get(), 11); +} + +#[test] +fn test_iterator_array_chunks_rfold() { + let result = (0..10).array_chunks::<3>().rfold(0, |acc, arr| { + assert_eq!(arr, [7 - (acc * 3), 8 - (acc * 3), 9 - (acc * 3)]); + acc + 1 + }); + assert_eq!(result, 3); + + let counter = Cell::new(0); + (0..10).map(|_| DropBomb::new(&counter)).array_chunks::<3>().rfold(0, |acc, _arr| { + assert_eq!(counter.get(), acc * 3); + acc + 1 + }); + assert_eq!(counter.get(), 10); +} diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs index 567d9fe49cade..a546e72a7b737 100644 --- a/library/core/tests/iter/adapters/mod.rs +++ b/library/core/tests/iter/adapters/mod.rs @@ -1,3 +1,4 @@ +mod array_chunks; mod chain; mod cloned; mod copied; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index c7756a503c3e9..380ac8ec26af5 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -45,6 +45,7 @@ #![feature(slice_partition_dedup)] #![feature(int_log)] #![feature(iter_advance_by)] +#![feature(iter_array_chunks)] #![feature(iter_partition_in_place)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)]