Skip to content

Add PeekableIterator trait #144935

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

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
9 changes: 8 additions & 1 deletion library/core/src/array/iter.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Defines the `IntoIter` owned iterator for arrays.

use crate::intrinsics::transmute_unchecked;
use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce};
use crate::iter::{FusedIterator, PeekableIterator, TrustedLen, TrustedRandomAccessNoCoerce};
use crate::mem::MaybeUninit;
use crate::num::NonZero;
use crate::ops::{IndexRange, Range, Try};
Expand Down Expand Up @@ -350,6 +350,13 @@ impl<T, const N: usize> FusedIterator for IntoIter<T, N> {}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, N> {}

#[unstable(feature = "peekable_iterator", issue = "132973")]
impl<T, const N: usize> PeekableIterator for IntoIter<T, N> {
fn peek_with<U>(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> U) -> U {
self.unsize_mut().peek_with(func)
}
}

#[doc(hidden)]
#[unstable(issue = "none", feature = "std_internals")]
#[rustc_unsafe_specialization_marker]
Expand Down
10 changes: 10 additions & 0 deletions library/core/src/array/iter/iter_inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,16 @@ impl<T> PolymorphicIter<[MaybeUninit<T>]> {
})
}

#[inline]
pub(super) fn peek_with<U>(&mut self, func: impl for<'b> FnOnce(Option<&'b T>) -> U) -> U {
let tmp = self.alive.clone().next().map(|idx| {
// SAFETY: `idx` is in self.alive range
unsafe { self.data.get_unchecked(idx).assume_init_read() }
});

func(tmp.as_ref())
}

#[inline]
pub(super) fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ pub use self::traits::FusedIterator;
pub use self::traits::InPlaceIterable;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::traits::Iterator;
#[unstable(feature = "peekable_iterator", issue = "132973")]
pub use self::traits::PeekableIterator;
#[unstable(issue = "none", feature = "trusted_fused")]
pub use self::traits::TrustedFused;
#[unstable(feature = "trusted_len", issue = "37572")]
Expand Down
3 changes: 3 additions & 0 deletions library/core/src/iter/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod double_ended;
mod exact_size;
mod iterator;
mod marker;
mod peekable;
mod unchecked_iterator;

#[unstable(issue = "none", feature = "inplace_iteration")]
Expand All @@ -12,6 +13,8 @@ pub use self::marker::InPlaceIterable;
pub use self::marker::TrustedFused;
#[unstable(feature = "trusted_step", issue = "85731")]
pub use self::marker::TrustedStep;
#[unstable(feature = "peekable_iterator", issue = "132973")]
pub use self::peekable::PeekableIterator;
pub(crate) use self::unchecked_iterator::UncheckedIterator;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::{
Expand Down
28 changes: 28 additions & 0 deletions library/core/src/iter/traits/peekable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#[unstable(feature = "peekable_iterator", issue = "132973")]
/// Iterators which inherently support peeking without needing to be wrapped by a `Peekable`.
pub trait PeekableIterator: Iterator {
/// Executes the closure with a reference to the `next()` value without advancing the iterator.
fn peek_with<T>(&mut self, func: impl for<'a> FnOnce(Option<&'a Self::Item>) -> T) -> T;

/// Executes the closure on the `next()` element without advancing the iterator, or returns `None` if the iterator is exhausted.
fn peek_map<T>(&mut self, func: impl for<'a> FnOnce(&'a Self::Item) -> T) -> Option<T> {
self.peek_with(|x| x.map(|y| func(y)))
}

/// Returns the `next()` element if the given predicate holds true.
fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option<Self::Item> {
match self.peek_map(func) {
Some(true) => self.next(),
_ => None,
}
}

/// Moves forward and return the `next()` item if it is equal to the expected value.
fn next_if_eq<T>(&mut self, expected: &T) -> Option<Self::Item>
where
Self::Item: PartialEq<T>,
T: ?Sized,
{
self.next_if(|x| x == expected)
}
}
3 changes: 2 additions & 1 deletion library/core/src/slice/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ mod macros;
use super::{from_raw_parts, from_raw_parts_mut};
use crate::hint::assert_unchecked;
use crate::iter::{
FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, UncheckedIterator,
FusedIterator, PeekableIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
UncheckedIterator,
};
use crate::marker::PhantomData;
use crate::mem::{self, SizedTypeProperties};
Expand Down
28 changes: 28 additions & 0 deletions library/core/src/slice/iter/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,34 @@ macro_rules! iterator {
}
}

#[unstable(feature = "peekable_iterator", issue = "132973")]
impl<'a, T> PeekableIterator for $name<'a, T> {
fn peek_with<U>(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> U) -> U {
let ptr = self.ptr;
let end_or_len = self.end_or_len;

// SAFETY: See inner comments.
unsafe {
if T::IS_ZST {
let len = end_or_len.addr();
if len == 0 {
return func(None);
}
} else {
// SAFETY: by type invariant, the `end_or_len` field is always
// non-null for a non-ZST pointee. (This transmute ensures we
// get `!nonnull` metadata on the load of the field.)
if ptr == crate::intrinsics::transmute::<$ptr, NonNull<T>>(end_or_len) {
return func(None);
}
}
// SAFETY: Now that we know it wasn't empty
// we can give out a reference to it.
let tmp = {ptr}.$into_ref();
func(Some(&tmp))
}
}
}
#[stable(feature = "default_iters", since = "1.70.0")]
impl<T> Default for $name<'_, T> {
/// Creates an empty slice iterator.
Expand Down
12 changes: 10 additions & 2 deletions library/core/src/str/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use super::{
};
use crate::fmt::{self, Write};
use crate::iter::{
Chain, Copied, Filter, FlatMap, Flatten, FusedIterator, Map, TrustedLen, TrustedRandomAccess,
TrustedRandomAccessNoCoerce,
Chain, Copied, Filter, FlatMap, Flatten, FusedIterator, Map, PeekableIterator, TrustedLen,
TrustedRandomAccess, TrustedRandomAccessNoCoerce,
};
use crate::num::NonZero;
use crate::ops::Try;
Expand Down Expand Up @@ -132,6 +132,14 @@ impl<'a> DoubleEndedIterator for Chars<'a> {
}
}

#[unstable(feature = "peekable_iterator", issue = "132973")]
impl<'a> PeekableIterator for Chars<'a> {
fn peek_with<T>(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> T) -> T {
let tmp = self.clone().next();
func(tmp.as_ref())
}
}

#[stable(feature = "fused", since = "1.26.0")]
impl FusedIterator for Chars<'_> {}

Expand Down
Loading