-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Iterator::collect_array method #79659
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,9 @@ | |
// can't split that into multiple files. | ||
|
||
use crate::cmp::{self, Ordering}; | ||
use crate::mem::{self, MaybeUninit}; | ||
use crate::ops::{Add, ControlFlow, Try}; | ||
use crate::ptr; | ||
|
||
use super::super::TrustedRandomAccess; | ||
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; | ||
|
@@ -1670,6 +1672,77 @@ pub trait Iterator { | |
FromIterator::from_iter(self) | ||
} | ||
|
||
/// Collects all items from the iterator into an array of a specific size. | ||
/// | ||
/// If the number of elements inside the iterator is exactly equal to the | ||
/// array size, then the array is returned inside `Some`, otherwise `None` | ||
/// is returned. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(iter_collect_array)] | ||
/// | ||
/// let xs = [1, 2, 3]; | ||
/// let iter = xs.iter().copied(); | ||
/// let [_a, b, _c] = iter.clone().collect_array().unwrap(); | ||
/// assert_eq!(b, 2); | ||
/// | ||
/// match iter.collect_array() { | ||
/// Some([_, _]) => panic!("Didn't expect to see two only elements"), | ||
/// None => (), | ||
/// } | ||
/// ``` | ||
#[inline] | ||
#[unstable(reason = "new API", issue = "none", feature = "iter_collect_array")] | ||
fn collect_array<const N: usize>(self) -> Option<[Self::Item; N]> | ||
where | ||
Self: Sized, | ||
{ | ||
struct Buf<T, const N: usize> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have this type somewhere already? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess there is one in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like we need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's an item for the next libs meeting to talk about that https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/PRs.20need.20a.20decision.20on.20.60try_generate.60.20or.20.60ArrayVec.60.20or.20.2E.2E.2E/near/218779392 :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm currently writing (though it's a bit more convoluted since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately we rarely get to our needs decision items 🙃 If we keep tabs on these unwind-safe-partially-initialized-array types we're introducing I'd be happy not to block any of these PRs individually on them and run a pass over later to replace them with something more general. |
||
// Safety invariant: first `len` items are initialized. | ||
items: [MaybeUninit<T>; N], | ||
len: usize, | ||
} | ||
impl<T, const N: usize> Buf<T, N> { | ||
fn new() -> Buf<T, N> { | ||
Buf { items: MaybeUninit::uninit_array(), len: 0 } | ||
} | ||
fn push(&mut self, item: T) -> Option<()> { | ||
let slot = self.items.get_mut(self.len)?; | ||
slot.write(item); | ||
self.len += 1; | ||
Some(()) | ||
} | ||
fn into_array(mut self) -> Option<[T; N]> { | ||
if self.len != N { | ||
return None; | ||
} | ||
self.len = 0; | ||
let res = | ||
// SAFETY: `len` field invariant + the guard above. | ||
unsafe { mem::transmute_copy::<[MaybeUninit<T>; N], [T; N]>(&self.items) }; | ||
Some(res) | ||
} | ||
} | ||
|
||
impl<T, const N: usize> Drop for Buf<T, N> { | ||
fn drop(&mut self) { | ||
// SAFETY: `len` field invariant. | ||
unsafe { | ||
let slice = MaybeUninit::slice_assume_init_mut(&mut self.items[..self.len]); | ||
ptr::drop_in_place(slice); | ||
} | ||
} | ||
} | ||
|
||
let mut buf: Buf<Self::Item, { N }> = Buf::new(); | ||
for elem in self { | ||
buf.push(elem)?; | ||
} | ||
buf.into_array() | ||
} | ||
|
||
/// Consumes an iterator, creating two collections from it. | ||
/// | ||
/// The predicate passed to `partition()` can return `true`, or `false`. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pondering: given that this will potentially collect only a prefix, should it be
&mut self
? TheOption
is enough to know whether the iterator was exhausted in the call, so one could use.collect_array::<4>()
in a loop...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think I misunderstood initially... You want to have "exact N" semantics, but you want
&mut self
at the same time?This is interesting!
The problem I am seeing here is that, to get exactly N, you'll need to fetch N + 1 elements, so one would be dropped on the floor or stuffed into the
Err
variant somehow.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking that
it.collect_array::<3>()
would be equivalent totry { [it.next()?, it.next()?, it.next()?] }
. So you'd getNone
if the length is shorter than the number requested, but if there's more you'd be able to call again to get more of them.(Maybe with a rename like sfackler mentions -- this is reminding me of
as_chunks
andarray_chunks
on slices, so maybe something like that?)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@scottmcm for iterators,
self
and&mut self
is the same thing - you can always use.by_ref()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@WaffleLapkin Conceptually yes. But in implementation not exactly -- see all these benchmarks for example.
And if this doesn't exhaust the iterator, then it should be
&mut self
for consistency with other iterator methods that don't necessarily exhaust the iterator (likeall
andany
, which are also&mut self
).