Skip to content

Commit 84bc38a

Browse files
committed
Add Iterator::collect_array method
With const-generics, we can now collect exactly `N` elements out of the iterator. Very similar API existed in the itertools crate for quite some time: https://docs.rs/itertools/0.9.0/itertools/trait.Itertools.html#method.collect_tuple
1 parent c7cff21 commit 84bc38a

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

library/core/src/iter/traits/iterator.rs

+73
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
// can't split that into multiple files.
44

55
use crate::cmp::{self, Ordering};
6+
use crate::mem::{self, MaybeUninit};
67
use crate::ops::{Add, ControlFlow, Try};
8+
use crate::ptr;
79

810
use super::super::TrustedRandomAccess;
911
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse};
@@ -1670,6 +1672,77 @@ pub trait Iterator {
16701672
FromIterator::from_iter(self)
16711673
}
16721674

1675+
/// Collects all items from the iterator into an array of a specific size.
1676+
///
1677+
/// If the number of elements inside the iterator is exactly equal to the
1678+
/// array size, then the array is returned inside `Some`, otherwise `None`
1679+
/// is returned.
1680+
///
1681+
/// # Examples
1682+
///
1683+
/// ```
1684+
/// #![feature(iter_collect_array)]
1685+
///
1686+
/// let xs = [1, 2, 3];
1687+
/// let iter = xs.iter().copied();
1688+
/// let [_a, b, _c] = iter.clone().collect_array().unwrap();
1689+
/// assert_eq!(b, 2);
1690+
///
1691+
/// match iter.collect_array() {
1692+
/// Some([_, _]) => panic!("Didn't expect to see two only elements"),
1693+
/// None => (),
1694+
/// }
1695+
/// ```
1696+
#[inline]
1697+
#[unstable(reason = "new API", issue = "none", feature = "iter_collect_array")]
1698+
fn collect_array<const N: usize>(self) -> Option<[Self::Item; N]>
1699+
where
1700+
Self: Sized,
1701+
{
1702+
struct Buf<T, const N: usize> {
1703+
// Safety invariant: first `len` items are initialized.
1704+
items: [MaybeUninit<T>; N],
1705+
len: usize,
1706+
}
1707+
impl<T, const N: usize> Buf<T, N> {
1708+
fn new() -> Buf<T, N> {
1709+
Buf { items: MaybeUninit::uninit_array(), len: 0 }
1710+
}
1711+
fn push(&mut self, item: T) -> Option<()> {
1712+
let slot = self.items.get_mut(self.len)?;
1713+
slot.write(item);
1714+
self.len += 1;
1715+
Some(())
1716+
}
1717+
fn into_array(mut self) -> Option<[T; N]> {
1718+
if self.len != N {
1719+
return None;
1720+
}
1721+
self.len = 0;
1722+
let res =
1723+
// SAFETY: `len` field invariant + the guard above.
1724+
unsafe { mem::transmute_copy::<[MaybeUninit<T>; N], [T; N]>(&self.items) };
1725+
Some(res)
1726+
}
1727+
}
1728+
1729+
impl<T, const N: usize> Drop for Buf<T, N> {
1730+
fn drop(&mut self) {
1731+
// SAFETY: `len` field invariant.
1732+
unsafe {
1733+
let slice = MaybeUninit::slice_assume_init_mut(&mut self.items[..self.len]);
1734+
ptr::drop_in_place(slice);
1735+
}
1736+
}
1737+
}
1738+
1739+
let mut buf: Buf<Self::Item, { N }> = Buf::new();
1740+
for elem in self {
1741+
buf.push(elem)?;
1742+
}
1743+
buf.into_array()
1744+
}
1745+
16731746
/// Consumes an iterator, creating two collections from it.
16741747
///
16751748
/// The predicate passed to `partition()` can return `true`, or `false`.

0 commit comments

Comments
 (0)