Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions library/core/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::convert::Infallible;
use crate::error::Error;
use crate::hash::{self, Hash};
use crate::intrinsics::transmute_unchecked;
use crate::iter::{UncheckedIterator, repeat_n};
use crate::iter::{TrustedLen, UncheckedIterator, repeat_n};
use crate::marker::Destruct;
use crate::mem::{self, ManuallyDrop, MaybeUninit};
use crate::ops::{
Expand Down Expand Up @@ -964,6 +964,12 @@ impl<T: [const] Destruct> const Drop for Guard<'_, T> {
}
}

pub(crate) fn iter_next_chunk<T, const N: usize>(
iter: &mut impl Iterator<Item = T>,
) -> Result<[T; N], IntoIter<T, N>> {
iter.spec_next_chunk()
}
#[inline]
/// Pulls `N` items from `iter` and returns them as an array. If the iterator
/// yields fewer than `N` items, `Err` is returned containing an iterator over
/// the already yielded items.
Expand All @@ -976,8 +982,7 @@ impl<T: [const] Destruct> const Drop for Guard<'_, T> {
/// dropped.
///
/// Used for [`Iterator::next_chunk`].
#[inline]
pub(crate) fn iter_next_chunk<T, const N: usize>(
fn generic_iter_next_chunk<T, const N: usize>(
iter: &mut impl Iterator<Item = T>,
) -> Result<[T; N], IntoIter<T, N>> {
let mut array = [const { MaybeUninit::uninit() }; N];
Expand All @@ -994,6 +999,32 @@ pub(crate) fn iter_next_chunk<T, const N: usize>(
}
}

pub(crate) trait SpecNextChunk<T, const N: usize>: Iterator<Item = T> {
fn spec_next_chunk(&mut self) -> Result<[T; N], IntoIter<T, N>>;
}
impl<I: Iterator<Item = T>, T, const N: usize> SpecNextChunk<T, N> for I {
default fn spec_next_chunk(&mut self) -> Result<[T; N], IntoIter<T, N>> {
generic_iter_next_chunk(self)
}
}

impl<I: Iterator<Item = T> + TrustedLen, T, const N: usize> SpecNextChunk<T, N> for I {
fn spec_next_chunk(&mut self) -> Result<[T; N], IntoIter<T, N>> {
if self.size_hint().0 < N {
let mut array = [const { MaybeUninit::uninit() }; N];
let initialized =
// SAFETY: Has to error out; trusted len means that
// SAFETY: there may only be less than N elements
unsafe { iter_next_chunk_erased(&mut array, self).unwrap_err_unchecked() };
// SAFETY: Only the first `initialized` elements were populated
return Err(unsafe { IntoIter::new_unchecked(array, 0..initialized) });
} else {
// SAFETY: must be at least N elements; safe to unwrap N elements.
return Ok(from_fn(|_| unsafe { self.next().unwrap_unchecked() }));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsure: Since you're specializing anyway, maybe https://std.noratrieb.dev/x86_64-unknown-linux-gnu/core/iter/traits/unchecked_iterator/trait.UncheckedIterator.html#method.next_unchecked instead?

Suggested change
return Ok(from_fn(|_| unsafe { self.next().unwrap_unchecked() }));
return Ok(from_fn(|_| unsafe { self.next_unchecked() }));

}
}
}

/// Version of [`iter_next_chunk`] using a passed-in slice in order to avoid
/// needing to monomorphize for every array length.
///
Expand Down
Loading