Skip to content
Open
Show file tree
Hide file tree
Changes from 14 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
13 changes: 13 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,19 @@ impl<T> PolymorphicIter<[MaybeUninit<T>]> {
})
}

#[inline]
pub(super) fn peek_with<U>(&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() }
});

let out = func(tmp.as_ref());
// Avoid dropping before the item is consumed
crate::mem::forget(tmp);
out
}

#[inline]
pub(super) fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
Expand Down
10 changes: 9 additions & 1 deletion library/core/src/iter/adapters/peekable.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::iter::adapters::SourceIter;
use crate::iter::{FusedIterator, TrustedLen};
use crate::iter::{FusedIterator, PeekableIterator, TrustedLen};
use crate::ops::{ControlFlow, Try};

/// An iterator with a `peek()` that returns an optional reference to the next
Expand Down Expand Up @@ -322,6 +322,14 @@ impl<I: Iterator> Peekable<I> {
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<I> TrustedLen for Peekable<I> where I: TrustedLen {}

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

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: Iterator> SourceIter for Peekable<I>
where
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)))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seem to have been part of the ACP. Could you inline it into next_if, or make it a standalone private function?


/// 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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// 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) {
if ptr.as_ptr() == end_or_len {

Copy link
Contributor Author

@wmstack wmstack Aug 9, 2025

Choose a reason for hiding this comment

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

Hmm, I was not able to write it this way as Rust determines the types differ in their mutability. In general, this code was based on the next() method generic over both Iter and IterMut

Copy link
Contributor

@tgross35 tgross35 Sep 3, 2025

Choose a reason for hiding this comment

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

What exactly was the error? Seems possible to resolve by adding a $as_ptr metavar that is either as_ptr or as_mut_ptr depending on which one is being implemented (like into_ref).

next is a bit magical, others should try to be simpler if possible. cloning and advancing should be fine, it's cheap.

Copy link
Contributor

Choose a reason for hiding this comment

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

Cloning actually wouldn't work for IterMut since it doesn't implement it. But that actually brings up a good point; the safety comments need to say why it is sound to hand out a&mut T.

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