diff --git a/src/arrays.rs b/src/arrays.rs new file mode 100644 index 000000000..b31a00375 --- /dev/null +++ b/src/arrays.rs @@ -0,0 +1,52 @@ +use crate::Itertools; + +macro_rules! const_assert_positive { + ($N: ty) => { + trait StaticAssert { + const ASSERT: bool; + } + + impl StaticAssert for () { + const ASSERT: bool = { + assert!(N > 0); + true + }; + } + + assert!(<() as StaticAssert>::ASSERT); + }; +} + +/// An iterator that groups the items in arrays of const generic size `N`. +/// +/// See [`.next_array()`](crate::Itertools::next_array) for details. +#[derive(Debug, Clone)] +pub struct Arrays { + iter: I, +} + +impl Arrays { + pub(crate) fn new(iter: I) -> Self { + const_assert_positive!(N); + + // TODO should we use iter.fuse() instead? + Self { iter } + } +} + +impl Iterator for Arrays { + type Item = [I::Item; N]; + + fn next(&mut self) -> Option { + self.iter.next_array() + } + + fn size_hint(&self) -> (usize, Option) { + // also verified in `new()` + const_assert_positive!(N); + let (lo, hi) = self.iter.size_hint(); + (lo / N, hi.map(|hi| hi / N)) + } +} + +impl ExactSizeIterator for Arrays {} diff --git a/src/lib.rs b/src/lib.rs index 619f903bc..9e81bd212 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,7 @@ pub mod structs { }; pub use crate::all_equal_value_err::AllEqualValueError; pub use crate::array_impl::{ArrayWindows, CircularArrayWindows}; + pub use crate::arrays::Arrays; #[cfg(feature = "use_alloc")] pub use crate::combinations::{ArrayCombinations, Combinations}; #[cfg(feature = "use_alloc")] @@ -179,6 +180,7 @@ pub use crate::with_position::Position; pub use crate::ziptuple::multizip; mod adaptors; mod array_impl; +mod arrays; mod either_or_both; pub use crate::either_or_both::EitherOrBoth; #[doc(hidden)] @@ -790,6 +792,45 @@ pub trait Itertools: Iterator { groupbylazy::new_chunks(self, size) } + /// Return an iterator that groups the items in arrays of const generic size `N`. + /// + /// `N == 0` is a compile-time (but post-monomorphization) error. + /// + /// See also the method [`.next_array()`](Itertools::next_array). + /// + /// ```rust + /// use itertools::Itertools; + /// let mut v = Vec::new(); + /// for [a, b] in (1..5).arrays() { + /// v.push([a, b]); + /// } + /// assert_eq!(v, vec![[1, 2], [3, 4]]); + /// + /// // this requires a type hint + /// let it = (1..7).arrays::<3>(); + /// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]); + /// + /// // you can also specify the complete type + /// use itertools::Arrays; + /// use std::ops::Range; + /// + /// let it: Arrays, 3> = (1..7).arrays(); + /// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]); + /// ``` + /// + /// ```compile_fail + /// use itertools::Itertools; + /// + /// let mut it = (1..5).arrays::<0>(); + /// assert_eq!(Some([]), it.next()); + /// ``` + fn arrays(self) -> Arrays + where + Self: Sized, + { + Arrays::new(self) + } + /// Return an iterator over all contiguous windows producing tuples of /// a specific size (up to 12). /// diff --git a/tests/quick.rs b/tests/quick.rs index b4129a305..3ecdd34f4 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -1373,6 +1373,16 @@ quickcheck! { } } +quickcheck! { + fn arrays_exact_size_1(a: Vec) -> bool { + exact_size(a.iter().arrays::<1>()) + } + + fn arrays_exact_size_4(a: Vec) -> bool { + exact_size(a.iter().arrays::<4>()) + } +} + // with_position quickcheck! { fn with_position_exact_size_1(a: Vec) -> bool {