From 29080b4a095941e4c9e2cbfca901f0a41ec563bd Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sun, 2 Mar 2025 19:00:19 +0100 Subject: [PATCH 01/16] array_chunks --- src/array_chunks.rs | 82 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 55 ++++++++++++++++++++++++++++++ src/next_array.rs | 21 +++++++++++- 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/array_chunks.rs diff --git a/src/array_chunks.rs b/src/array_chunks.rs new file mode 100644 index 000000000..205dd557a --- /dev/null +++ b/src/array_chunks.rs @@ -0,0 +1,82 @@ +use alloc::vec::Vec; + +use crate::next_array::ArrayBuilder; + +/// 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 ArrayChunks { + iter: I, + partial: Vec, +} + +impl ArrayChunks { + pub(crate) fn new(iter: I) -> Self { + // TODO should we use iter.fuse() instead? Otherwise remainder may behave strangely + Self { + iter, + partial: Vec::new(), + } + } + + /// Returns an iterator that yields all the items that have + /// not been included in any of the arrays. Use this to access the + /// leftover elements if the total number of elements yielded by + /// the original iterator is not a multiple of `N`. + /// + /// If `self` is not exhausted (i.e. `next()` has not returned `None`) + /// then the iterator returned by `remainder()` will also include + /// the elements that *would* have been included in the arrays + /// produced by `next()`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut it = (1..9).array_chunks(); + /// assert_eq!(Some([1, 2, 3]), it.next()); + /// assert_eq!(Some([4, 5, 6]), it.next()); + /// assert_eq!(None, it.next()); + /// itertools::assert_equal(it.remainder(), [7,8]); + /// + /// let mut it = (1..9).array_chunks(); + /// assert_eq!(Some([1, 2, 3]), it.next()); + /// itertools::assert_equal(it.remainder(), 4..9); + /// ``` + pub fn remainder(self) -> impl Iterator { + self.partial.into_iter().chain(self.iter) + } +} + +impl Iterator for ArrayChunks { + type Item = [I::Item; N]; + + fn next(&mut self) -> Option { + if !self.partial.is_empty() { + return None; + } + let mut builder = ArrayBuilder::new(); + for _ in 0..N { + if let Some(item) = self.iter.next() { + builder.push(item); + } else { + break; + } + } + if let Some(array) = builder.take() { + Some(array) + } else { + self.partial = builder.into_vec(); + None + } + } + + fn size_hint(&self) -> (usize, Option) { + if N == 0 { + (usize::MAX, None) + } else { + let (lo, hi) = self.iter.size_hint(); + (lo / N, hi.map(|hi| hi / N)) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 619f903bc..80df70e2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,6 +102,8 @@ pub mod structs { pub use crate::all_equal_value_err::AllEqualValueError; pub use crate::array_impl::{ArrayWindows, CircularArrayWindows}; #[cfg(feature = "use_alloc")] + pub use crate::array_chunks::ArrayChunks; + #[cfg(feature = "use_alloc")] pub use crate::combinations::{ArrayCombinations, Combinations}; #[cfg(feature = "use_alloc")] pub use crate::combinations_with_replacement::CombinationsWithReplacement; @@ -179,6 +181,8 @@ pub use crate::with_position::Position; pub use crate::ziptuple::multizip; mod adaptors; mod array_impl; +#[cfg(feature = "use_alloc")] +mod array_chunks; mod either_or_both; pub use crate::either_or_both::EitherOrBoth; #[doc(hidden)] @@ -790,6 +794,57 @@ pub trait Itertools: Iterator { groupbylazy::new_chunks(self, size) } + /// Return an iterator that groups the items in arrays of const generic size `N`. + /// + /// Use the method `.remainder()` to access leftover items in case + /// the number of items yielded by the original iterator is not a multiple of `N`. + /// + /// If `N` is 0, the resulting iterator will be equivalent to `repeat([])`, i.e. + /// `next()` will always return `Some([])`. + /// + /// See also the method [`.next_array()`](Itertools::next_array). + /// + /// ``` + /// use itertools::Itertools; + /// let mut v = Vec::new(); + /// for [a, b] in (1..5).array_chunks() { + /// v.push([a, b]); + /// } + /// assert_eq!(v, vec![[1, 2], [3, 4]]); + /// + /// let mut it = (1..9).array_chunks(); + /// assert_eq!(Some([1, 2, 3]), it.next()); + /// assert_eq!(Some([4, 5, 6]), it.next()); + /// assert_eq!(None, it.next()); + /// itertools::assert_equal(it.remainder(), [7,8]); + /// + /// // this requires a type hint + /// let it = (1..7).array_chunks::<3>(); + /// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]); + /// + /// // you can also specify the complete type + /// use itertools::ArrayChunks; + /// use std::ops::Range; + /// + /// let it: ArrayChunks, 3> = (1..7).array_chunks(); + /// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]); + /// + /// let mut it = (1..3).array_chunks::<0>(); + /// assert_eq!(it.next(), Some([])); + /// assert_eq!(it.next(), Some([])); + /// // and so on for any further calls to `it.next()` + /// itertools::assert_equal(it.remainder(), 1..3); + /// ``` + /// + /// See also [`Tuples::into_buffer`]. + #[cfg(feature = "use_alloc")] + fn array_chunks(self) -> ArrayChunks + where + Self: Sized, + { + ArrayChunks::new(self) + } + /// Return an iterator over all contiguous windows producing tuples of /// a specific size (up to 12). /// diff --git a/src/next_array.rs b/src/next_array.rs index 86480b197..09e27fc6c 100644 --- a/src/next_array.rs +++ b/src/next_array.rs @@ -1,7 +1,9 @@ +#[cfg(feature = "use_alloc")] +use alloc::vec::Vec; use core::mem::{self, MaybeUninit}; /// An array of at most `N` elements. -struct ArrayBuilder { +pub(crate) struct ArrayBuilder { /// The (possibly uninitialized) elements of the `ArrayBuilder`. /// /// # Safety @@ -86,6 +88,23 @@ impl ArrayBuilder { None } } + + #[cfg(feature = "use_alloc")] + pub(crate) fn into_vec(mut self) -> Vec { + let len = self.len; + // SAFETY: Decreasing the value of `self.len` cannot violate the + // safety invariant on `self.arr`. + self.len = 0; + (0..len) + .map(|i| { + // SAFETY: Since `self.len` is 0, `self.arr` may safely contain + // uninitialized elements. + let item = mem::replace(&mut self.arr[i], MaybeUninit::uninit()); + // SAFETY: we know that item is valid since i < len + unsafe { item.assume_init() } + }) + .collect() + } } impl AsMut<[T]> for ArrayBuilder { From 37a77b8070c82e9bdc2981e872040ad40c2c8193 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 3 Mar 2025 00:12:03 +0100 Subject: [PATCH 02/16] copy-paste artefact --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 80df70e2c..8077b4e6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -835,8 +835,6 @@ pub trait Itertools: Iterator { /// // and so on for any further calls to `it.next()` /// itertools::assert_equal(it.remainder(), 1..3); /// ``` - /// - /// See also [`Tuples::into_buffer`]. #[cfg(feature = "use_alloc")] fn array_chunks(self) -> ArrayChunks where From e3af5fbec1c1fb4c16c20d02a62d25a1bddfe8bc Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 3 Mar 2025 00:18:14 +0100 Subject: [PATCH 03/16] PME on array_chunks::<0> --- src/array_chunks.rs | 3 +++ src/lib.rs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/array_chunks.rs b/src/array_chunks.rs index 205dd557a..faed3c6bf 100644 --- a/src/array_chunks.rs +++ b/src/array_chunks.rs @@ -13,6 +13,9 @@ pub struct ArrayChunks { impl ArrayChunks { pub(crate) fn new(iter: I) -> Self { + const { + assert!(N > 0); + } // TODO should we use iter.fuse() instead? Otherwise remainder may behave strangely Self { iter, diff --git a/src/lib.rs b/src/lib.rs index 8077b4e6b..2e2fd46ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -799,12 +799,11 @@ pub trait Itertools: Iterator { /// Use the method `.remainder()` to access leftover items in case /// the number of items yielded by the original iterator is not a multiple of `N`. /// - /// If `N` is 0, the resulting iterator will be equivalent to `repeat([])`, i.e. - /// `next()` will always return `Some([])`. + /// `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).array_chunks() { @@ -828,12 +827,13 @@ pub trait Itertools: Iterator { /// /// let it: ArrayChunks, 3> = (1..7).array_chunks(); /// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]); + /// ``` + /// + /// ```compile_fail + /// use itertools::Itertools; /// - /// let mut it = (1..3).array_chunks::<0>(); - /// assert_eq!(it.next(), Some([])); - /// assert_eq!(it.next(), Some([])); - /// // and so on for any further calls to `it.next()` - /// itertools::assert_equal(it.remainder(), 1..3); + /// let mut it = (1..5).array_chunks::<0>(); + /// assert_eq!(Some([]), it.next()); /// ``` #[cfg(feature = "use_alloc")] fn array_chunks(self) -> ArrayChunks From 3aa4f604472bf4aa33c887a77f9baa1bf2abc8ac Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 3 Mar 2025 00:28:13 +0100 Subject: [PATCH 04/16] impl ExactSizeIterator --- src/array_chunks.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/array_chunks.rs b/src/array_chunks.rs index faed3c6bf..380a9288e 100644 --- a/src/array_chunks.rs +++ b/src/array_chunks.rs @@ -83,3 +83,5 @@ impl Iterator for ArrayChunks { } } } + +impl ExactSizeIterator for ArrayChunks {} From add6f83a45199a1c6f76cd240f07a23e1b2a791c Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 3 Mar 2025 00:32:47 +0100 Subject: [PATCH 05/16] exact size tests --- src/array_chunks.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/array_chunks.rs b/src/array_chunks.rs index 380a9288e..507e5c7bf 100644 --- a/src/array_chunks.rs +++ b/src/array_chunks.rs @@ -85,3 +85,34 @@ impl Iterator for ArrayChunks { } impl ExactSizeIterator for ArrayChunks {} + +#[cfg(test)] +mod tests { + use crate::Itertools; + + fn exact_size_helper(it: impl Iterator) { + let (lo, hi) = it.size_hint(); + let count = it.count(); + assert_eq!(lo, count); + assert_eq!(hi, Some(count)); + } + + #[test] + fn exact_size_not_divisible() { + let it = (0..10).array_chunks::<3>(); + exact_size_helper(it); + } + + #[test] + fn exact_size_after_next() { + let mut it = (0..10).array_chunks::<3>(); + _ = it.next(); + exact_size_helper(it); + } + + #[test] + fn exact_size_divisible() { + let it = (0..10).array_chunks::<5>(); + exact_size_helper(it); + } +} From d3db791bb8e30a01debef5b1c831d85bf9439dcd Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 3 Mar 2025 00:39:48 +0100 Subject: [PATCH 06/16] rename to arrays Iterator::array_chunks exists on nightly and it's better to avoid the name collision especially if and when that's stabilized --- src/{array_chunks.rs => arrays.rs} | 12 ++++++------ src/lib.rs | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) rename src/{array_chunks.rs => arrays.rs} (91%) diff --git a/src/array_chunks.rs b/src/arrays.rs similarity index 91% rename from src/array_chunks.rs rename to src/arrays.rs index 507e5c7bf..42be5be7b 100644 --- a/src/array_chunks.rs +++ b/src/arrays.rs @@ -6,12 +6,12 @@ use crate::next_array::ArrayBuilder; /// /// See [`.next_array()`](crate::Itertools::next_array) for details. #[derive(Debug, Clone)] -pub struct ArrayChunks { +pub struct Arrays { iter: I, partial: Vec, } -impl ArrayChunks { +impl Arrays { pub(crate) fn new(iter: I) -> Self { const { assert!(N > 0); @@ -36,13 +36,13 @@ impl ArrayChunks { /// ``` /// use itertools::Itertools; /// - /// let mut it = (1..9).array_chunks(); + /// let mut it = (1..9).arrays(); /// assert_eq!(Some([1, 2, 3]), it.next()); /// assert_eq!(Some([4, 5, 6]), it.next()); /// assert_eq!(None, it.next()); /// itertools::assert_equal(it.remainder(), [7,8]); /// - /// let mut it = (1..9).array_chunks(); + /// let mut it = (1..9).arrays(); /// assert_eq!(Some([1, 2, 3]), it.next()); /// itertools::assert_equal(it.remainder(), 4..9); /// ``` @@ -51,7 +51,7 @@ impl ArrayChunks { } } -impl Iterator for ArrayChunks { +impl Iterator for Arrays { type Item = [I::Item; N]; fn next(&mut self) -> Option { @@ -84,7 +84,7 @@ impl Iterator for ArrayChunks { } } -impl ExactSizeIterator for ArrayChunks {} +impl ExactSizeIterator for Arrays {} #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 2e2fd46ea..bc85e544b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,7 +102,7 @@ pub mod structs { pub use crate::all_equal_value_err::AllEqualValueError; pub use crate::array_impl::{ArrayWindows, CircularArrayWindows}; #[cfg(feature = "use_alloc")] - pub use crate::array_chunks::ArrayChunks; + pub use crate::arrays::Arrays; #[cfg(feature = "use_alloc")] pub use crate::combinations::{ArrayCombinations, Combinations}; #[cfg(feature = "use_alloc")] @@ -182,7 +182,7 @@ pub use crate::ziptuple::multizip; mod adaptors; mod array_impl; #[cfg(feature = "use_alloc")] -mod array_chunks; +mod arrays; mod either_or_both; pub use crate::either_or_both::EitherOrBoth; #[doc(hidden)] @@ -806,41 +806,41 @@ pub trait Itertools: Iterator { /// ```rust /// use itertools::Itertools; /// let mut v = Vec::new(); - /// for [a, b] in (1..5).array_chunks() { + /// for [a, b] in (1..5).arrays() { /// v.push([a, b]); /// } /// assert_eq!(v, vec![[1, 2], [3, 4]]); /// - /// let mut it = (1..9).array_chunks(); + /// let mut it = (1..9).arrays(); /// assert_eq!(Some([1, 2, 3]), it.next()); /// assert_eq!(Some([4, 5, 6]), it.next()); /// assert_eq!(None, it.next()); /// itertools::assert_equal(it.remainder(), [7,8]); /// /// // this requires a type hint - /// let it = (1..7).array_chunks::<3>(); + /// 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::ArrayChunks; + /// use itertools::Arrays; /// use std::ops::Range; /// - /// let it: ArrayChunks, 3> = (1..7).array_chunks(); + /// 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).array_chunks::<0>(); + /// let mut it = (1..5).arrays::<0>(); /// assert_eq!(Some([]), it.next()); /// ``` #[cfg(feature = "use_alloc")] - fn array_chunks(self) -> ArrayChunks + fn arrays(self) -> Arrays where Self: Sized, { - ArrayChunks::new(self) + Arrays::new(self) } /// Return an iterator over all contiguous windows producing tuples of From e1d5072b36041b00147e8e84265beb1ecbbedd54 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Mon, 3 Mar 2025 00:43:43 +0100 Subject: [PATCH 07/16] avoid const assert to keep msrv --- src/arrays.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/arrays.rs b/src/arrays.rs index 42be5be7b..7563ba506 100644 --- a/src/arrays.rs +++ b/src/arrays.rs @@ -13,9 +13,8 @@ pub struct Arrays { impl Arrays { pub(crate) fn new(iter: I) -> Self { - const { - assert!(N > 0); - } + assert_positive::(); + // TODO should we use iter.fuse() instead? Otherwise remainder may behave strangely Self { iter, @@ -86,6 +85,22 @@ impl Iterator for Arrays { impl ExactSizeIterator for Arrays {} +/// Effectively assert!(N > 0) post-monomorphization +fn assert_positive() { + trait StaticAssert { + const ASSERT: bool; + } + + impl StaticAssert for () { + const ASSERT: bool = { + assert!(N > 0); + true + }; + } + + assert!(<() as StaticAssert>::ASSERT); +} + #[cfg(test)] mod tests { use crate::Itertools; From eb82706ab2bf5cb5a9c749d8c4dd1a540b8beb0f Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Wed, 5 Mar 2025 16:39:04 +0100 Subject: [PATCH 08/16] make assert_positive a macro --- src/arrays.rs | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/arrays.rs b/src/arrays.rs index 7563ba506..c955c5f86 100644 --- a/src/arrays.rs +++ b/src/arrays.rs @@ -2,6 +2,23 @@ use alloc::vec::Vec; use crate::next_array::ArrayBuilder; +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. @@ -13,7 +30,7 @@ pub struct Arrays { impl Arrays { pub(crate) fn new(iter: I) -> Self { - assert_positive::(); + const_assert_positive!(N); // TODO should we use iter.fuse() instead? Otherwise remainder may behave strangely Self { @@ -85,22 +102,6 @@ impl Iterator for Arrays { impl ExactSizeIterator for Arrays {} -/// Effectively assert!(N > 0) post-monomorphization -fn assert_positive() { - trait StaticAssert { - const ASSERT: bool; - } - - impl StaticAssert for () { - const ASSERT: bool = { - assert!(N > 0); - true - }; - } - - assert!(<() as StaticAssert>::ASSERT); -} - #[cfg(test)] mod tests { use crate::Itertools; From 1efe4a8e28fc92d540a8db20b91d3cd56855e0b2 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sat, 25 Jul 2026 05:25:53 -0400 Subject: [PATCH 09/16] remove remainder() --- src/arrays.rs | 45 ++------------------------------------------- src/lib.rs | 9 --------- 2 files changed, 2 insertions(+), 52 deletions(-) diff --git a/src/arrays.rs b/src/arrays.rs index c955c5f86..2e8cebf54 100644 --- a/src/arrays.rs +++ b/src/arrays.rs @@ -1,5 +1,3 @@ -use alloc::vec::Vec; - use crate::next_array::ArrayBuilder; macro_rules! const_assert_positive { @@ -25,7 +23,6 @@ macro_rules! const_assert_positive { #[derive(Debug, Clone)] pub struct Arrays { iter: I, - partial: Vec, } impl Arrays { @@ -33,37 +30,7 @@ impl Arrays { const_assert_positive!(N); // TODO should we use iter.fuse() instead? Otherwise remainder may behave strangely - Self { - iter, - partial: Vec::new(), - } - } - - /// Returns an iterator that yields all the items that have - /// not been included in any of the arrays. Use this to access the - /// leftover elements if the total number of elements yielded by - /// the original iterator is not a multiple of `N`. - /// - /// If `self` is not exhausted (i.e. `next()` has not returned `None`) - /// then the iterator returned by `remainder()` will also include - /// the elements that *would* have been included in the arrays - /// produced by `next()`. - /// - /// ``` - /// use itertools::Itertools; - /// - /// let mut it = (1..9).arrays(); - /// assert_eq!(Some([1, 2, 3]), it.next()); - /// assert_eq!(Some([4, 5, 6]), it.next()); - /// assert_eq!(None, it.next()); - /// itertools::assert_equal(it.remainder(), [7,8]); - /// - /// let mut it = (1..9).arrays(); - /// assert_eq!(Some([1, 2, 3]), it.next()); - /// itertools::assert_equal(it.remainder(), 4..9); - /// ``` - pub fn remainder(self) -> impl Iterator { - self.partial.into_iter().chain(self.iter) + Self { iter } } } @@ -71,9 +38,6 @@ impl Iterator for Arrays { type Item = [I::Item; N]; fn next(&mut self) -> Option { - if !self.partial.is_empty() { - return None; - } let mut builder = ArrayBuilder::new(); for _ in 0..N { if let Some(item) = self.iter.next() { @@ -82,12 +46,7 @@ impl Iterator for Arrays { break; } } - if let Some(array) = builder.take() { - Some(array) - } else { - self.partial = builder.into_vec(); - None - } + builder.take() } fn size_hint(&self) -> (usize, Option) { diff --git a/src/lib.rs b/src/lib.rs index bc85e544b..e9f2d826c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -796,9 +796,6 @@ pub trait Itertools: Iterator { /// Return an iterator that groups the items in arrays of const generic size `N`. /// - /// Use the method `.remainder()` to access leftover items in case - /// the number of items yielded by the original iterator is not a multiple of `N`. - /// /// `N == 0` is a compile-time (but post-monomorphization) error. /// /// See also the method [`.next_array()`](Itertools::next_array). @@ -811,12 +808,6 @@ pub trait Itertools: Iterator { /// } /// assert_eq!(v, vec![[1, 2], [3, 4]]); /// - /// let mut it = (1..9).arrays(); - /// assert_eq!(Some([1, 2, 3]), it.next()); - /// assert_eq!(Some([4, 5, 6]), it.next()); - /// assert_eq!(None, it.next()); - /// itertools::assert_equal(it.remainder(), [7,8]); - /// /// // this requires a type hint /// let it = (1..7).arrays::<3>(); /// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]); From 00b8c1e23268ff01040a12c18b7203f7fa6899d5 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sat, 25 Jul 2026 05:27:04 -0400 Subject: [PATCH 10/16] reuse existing next_array() --- src/arrays.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/arrays.rs b/src/arrays.rs index 2e8cebf54..dea940f11 100644 --- a/src/arrays.rs +++ b/src/arrays.rs @@ -1,4 +1,4 @@ -use crate::next_array::ArrayBuilder; +use crate::Itertools; macro_rules! const_assert_positive { ($N: ty) => { @@ -38,15 +38,7 @@ impl Iterator for Arrays { type Item = [I::Item; N]; fn next(&mut self) -> Option { - let mut builder = ArrayBuilder::new(); - for _ in 0..N { - if let Some(item) = self.iter.next() { - builder.push(item); - } else { - break; - } - } - builder.take() + self.iter.next_array() } fn size_hint(&self) -> (usize, Option) { From 6a2f8322f0a7d76ffde6c072508e0deda16eab6b Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sat, 25 Jul 2026 05:26:08 -0400 Subject: [PATCH 11/16] don't require alloc --- src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e9f2d826c..9e81bd212 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,7 +101,6 @@ pub mod structs { }; pub use crate::all_equal_value_err::AllEqualValueError; pub use crate::array_impl::{ArrayWindows, CircularArrayWindows}; - #[cfg(feature = "use_alloc")] pub use crate::arrays::Arrays; #[cfg(feature = "use_alloc")] pub use crate::combinations::{ArrayCombinations, Combinations}; @@ -181,7 +180,6 @@ pub use crate::with_position::Position; pub use crate::ziptuple::multizip; mod adaptors; mod array_impl; -#[cfg(feature = "use_alloc")] mod arrays; mod either_or_both; pub use crate::either_or_both::EitherOrBoth; @@ -826,7 +824,6 @@ pub trait Itertools: Iterator { /// let mut it = (1..5).arrays::<0>(); /// assert_eq!(Some([]), it.next()); /// ``` - #[cfg(feature = "use_alloc")] fn arrays(self) -> Arrays where Self: Sized, From a71605bd865f81774551675ebec56503f715a785 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sat, 25 Jul 2026 05:31:43 -0400 Subject: [PATCH 12/16] update TODO comment --- src/arrays.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arrays.rs b/src/arrays.rs index dea940f11..dc51ba314 100644 --- a/src/arrays.rs +++ b/src/arrays.rs @@ -29,7 +29,7 @@ impl Arrays { pub(crate) fn new(iter: I) -> Self { const_assert_positive!(N); - // TODO should we use iter.fuse() instead? Otherwise remainder may behave strangely + // TODO should we use iter.fuse() instead? Self { iter } } } From 41a68ace3f101d060d178684591b264e9e80bcf3 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sat, 25 Jul 2026 06:39:07 -0400 Subject: [PATCH 13/16] revert changes to ArrayBuilder --- src/next_array.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/next_array.rs b/src/next_array.rs index 09e27fc6c..86480b197 100644 --- a/src/next_array.rs +++ b/src/next_array.rs @@ -1,9 +1,7 @@ -#[cfg(feature = "use_alloc")] -use alloc::vec::Vec; use core::mem::{self, MaybeUninit}; /// An array of at most `N` elements. -pub(crate) struct ArrayBuilder { +struct ArrayBuilder { /// The (possibly uninitialized) elements of the `ArrayBuilder`. /// /// # Safety @@ -88,23 +86,6 @@ impl ArrayBuilder { None } } - - #[cfg(feature = "use_alloc")] - pub(crate) fn into_vec(mut self) -> Vec { - let len = self.len; - // SAFETY: Decreasing the value of `self.len` cannot violate the - // safety invariant on `self.arr`. - self.len = 0; - (0..len) - .map(|i| { - // SAFETY: Since `self.len` is 0, `self.arr` may safely contain - // uninitialized elements. - let item = mem::replace(&mut self.arr[i], MaybeUninit::uninit()); - // SAFETY: we know that item is valid since i < len - unsafe { item.assume_init() } - }) - .collect() - } } impl AsMut<[T]> for ArrayBuilder { From 66243aaa55c910c26703444aab30366d7ab2d578 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sat, 25 Jul 2026 08:38:30 -0400 Subject: [PATCH 14/16] remove N = 0 check in size_hint() --- src/arrays.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/arrays.rs b/src/arrays.rs index dc51ba314..5f4e7e876 100644 --- a/src/arrays.rs +++ b/src/arrays.rs @@ -42,12 +42,9 @@ impl Iterator for Arrays { } fn size_hint(&self) -> (usize, Option) { - if N == 0 { - (usize::MAX, None) - } else { - let (lo, hi) = self.iter.size_hint(); - (lo / N, hi.map(|hi| hi / N)) - } + let (lo, hi) = self.iter.size_hint(); + // `N` guaranteed to be positive in `new()` + (lo / N, hi.map(|hi| hi / N)) } } From bec8ee371642e1733d175c7e90116655a5813fb0 Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sat, 25 Jul 2026 09:05:49 -0400 Subject: [PATCH 15/16] move exact size tests to quickcheck --- src/arrays.rs | 31 ------------------------------- tests/quick.rs | 10 ++++++++++ 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/arrays.rs b/src/arrays.rs index 5f4e7e876..e224e9766 100644 --- a/src/arrays.rs +++ b/src/arrays.rs @@ -49,34 +49,3 @@ impl Iterator for Arrays { } impl ExactSizeIterator for Arrays {} - -#[cfg(test)] -mod tests { - use crate::Itertools; - - fn exact_size_helper(it: impl Iterator) { - let (lo, hi) = it.size_hint(); - let count = it.count(); - assert_eq!(lo, count); - assert_eq!(hi, Some(count)); - } - - #[test] - fn exact_size_not_divisible() { - let it = (0..10).array_chunks::<3>(); - exact_size_helper(it); - } - - #[test] - fn exact_size_after_next() { - let mut it = (0..10).array_chunks::<3>(); - _ = it.next(); - exact_size_helper(it); - } - - #[test] - fn exact_size_divisible() { - let it = (0..10).array_chunks::<5>(); - exact_size_helper(it); - } -} 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 { From e923eb1a164296200286dad3b1440f14b99f8e3d Mon Sep 17 00:00:00 2001 From: Ronno Das Date: Sun, 26 Jul 2026 20:45:42 -0400 Subject: [PATCH 16/16] assert N > 0 in size_hint() --- src/arrays.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/arrays.rs b/src/arrays.rs index e224e9766..b31a00375 100644 --- a/src/arrays.rs +++ b/src/arrays.rs @@ -42,8 +42,9 @@ impl Iterator for Arrays { } fn size_hint(&self) -> (usize, Option) { + // also verified in `new()` + const_assert_positive!(N); let (lo, hi) = self.iter.size_hint(); - // `N` guaranteed to be positive in `new()` (lo / N, hi.map(|hi| hi / N)) } }