Skip to content

Commit 81d085b

Browse files
orlpjswrenn
authored andcommitted
Added next_array and collect_array.
1 parent 1c850ce commit 81d085b

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed

src/lib.rs

+45
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ mod merge_join;
206206
mod minmax;
207207
#[cfg(feature = "use_alloc")]
208208
mod multipeek_impl;
209+
mod next_array;
209210
mod pad_tail;
210211
#[cfg(feature = "use_alloc")]
211212
mod peek_nth;
@@ -1914,6 +1915,50 @@ pub trait Itertools: Iterator {
19141915
}
19151916

19161917
// non-adaptor methods
1918+
/// Advances the iterator and returns the next items grouped in an array of
1919+
/// a specific size.
1920+
///
1921+
/// If there are enough elements to be grouped in an array, then the array
1922+
/// is returned inside `Some`, otherwise `None` is returned.
1923+
///
1924+
/// ```
1925+
/// use itertools::Itertools;
1926+
///
1927+
/// let mut iter = 1..5;
1928+
///
1929+
/// assert_eq!(Some([1, 2]), iter.next_array());
1930+
/// ```
1931+
fn next_array<T, const N: usize>(&mut self) -> Option<[T; N]>
1932+
where
1933+
Self: Sized + Iterator<Item = T>,
1934+
{
1935+
next_array::next_array(self)
1936+
}
1937+
1938+
/// Collects all items from the iterator into an array of a specific size.
1939+
///
1940+
/// If the number of elements inside the iterator is **exactly** equal to
1941+
/// the array size, then the array is returned inside `Some`, otherwise
1942+
/// `None` is returned.
1943+
///
1944+
/// ```
1945+
/// use itertools::Itertools;
1946+
///
1947+
/// let iter = 1..3;
1948+
///
1949+
/// if let Some([x, y]) = iter.collect_array() {
1950+
/// assert_eq!([x, y], [1, 2])
1951+
/// } else {
1952+
/// panic!("Expected two elements")
1953+
/// }
1954+
/// ```
1955+
fn collect_array<T, const N: usize>(mut self) -> Option<[T; N]>
1956+
where
1957+
Self: Sized + Iterator<Item = T>,
1958+
{
1959+
self.next_array().filter(|_| self.next().is_none())
1960+
}
1961+
19171962
/// Advances the iterator and returns the next items grouped in a tuple of
19181963
/// a specific size (up to 12).
19191964
///

src/next_array.rs

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use core::mem::MaybeUninit;
2+
use core::ptr;
3+
4+
/// Helper struct to build up an array element by element.
5+
struct ArrayBuilder<T, const N: usize> {
6+
arr: [MaybeUninit<T>; N], // Invariant: arr[..len] is initialized.
7+
len: usize, // Invariant: len <= N.
8+
}
9+
10+
impl<T, const N: usize> ArrayBuilder<T, N> {
11+
pub fn new() -> Self {
12+
Self {
13+
arr: [(); N].map(|_| MaybeUninit::uninit()),
14+
len: 0,
15+
}
16+
}
17+
18+
pub fn push(&mut self, value: T) {
19+
// We maintain the invariant here that arr[..len] is initialized.
20+
// Indexing with self.len also ensures self.len < N, and thus <= N after
21+
// the increment.
22+
self.arr[self.len] = MaybeUninit::new(value);
23+
self.len += 1;
24+
}
25+
26+
pub fn take(&mut self) -> Option<[T; N]> {
27+
if self.len == N {
28+
// Take the array, resetting the length back to zero.
29+
let arr = core::mem::replace(&mut self.arr, [(); N].map(|_| MaybeUninit::uninit()));
30+
self.len = 0;
31+
32+
// SAFETY: we had len == N, so all elements in arr are initialized.
33+
Some(unsafe { arr.map(|v| v.assume_init()) })
34+
} else {
35+
None
36+
}
37+
}
38+
}
39+
40+
impl<T, const N: usize> Drop for ArrayBuilder<T, N> {
41+
fn drop(&mut self) {
42+
unsafe {
43+
// SAFETY: arr[..len] is initialized, so must be dropped.
44+
// First we create a pointer to this initialized slice, then drop
45+
// that slice in-place. The cast from *mut MaybeUninit<T> to *mut T
46+
// is always sound by the layout guarantees of MaybeUninit.
47+
let ptr_to_first: *mut MaybeUninit<T> = self.arr.as_mut_ptr();
48+
let ptr_to_slice = ptr::slice_from_raw_parts_mut(ptr_to_first.cast::<T>(), self.len);
49+
ptr::drop_in_place(ptr_to_slice);
50+
}
51+
}
52+
}
53+
54+
/// Equivalent to `it.next_array()`.
55+
pub fn next_array<I, T, const N: usize>(it: &mut I) -> Option<[T; N]>
56+
where
57+
I: Iterator<Item = T>,
58+
{
59+
let mut builder = ArrayBuilder::new();
60+
for _ in 0..N {
61+
builder.push(it.next()?);
62+
}
63+
builder.take()
64+
}

tests/test_core.rs

+25
Original file line numberDiff line numberDiff line change
@@ -372,3 +372,28 @@ fn product1() {
372372
assert_eq!(v[1..3].iter().cloned().product1::<i32>(), Some(2));
373373
assert_eq!(v[1..5].iter().cloned().product1::<i32>(), Some(24));
374374
}
375+
376+
#[test]
377+
fn next_array() {
378+
let v = [1, 2, 3, 4, 5];
379+
let mut iter = v.iter();
380+
assert_eq!(iter.next_array(), Some([]));
381+
assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([1, 2]));
382+
assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([3, 4]));
383+
assert_eq!(iter.next_array::<_, 2>(), None);
384+
}
385+
386+
#[test]
387+
fn collect_array() {
388+
let v = [1, 2];
389+
let iter = v.iter().cloned();
390+
assert_eq!(iter.collect_array(), Some([1, 2]));
391+
392+
let v = [1];
393+
let iter = v.iter().cloned();
394+
assert_eq!(iter.collect_array::<_, 2>(), None);
395+
396+
let v = [1, 2, 3];
397+
let iter = v.iter().cloned();
398+
assert_eq!(iter.collect_array::<_, 2>(), None);
399+
}

0 commit comments

Comments
 (0)