From a2625c9bd0595709687fcb1af25cae67097b6bd2 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Tue, 5 Aug 2025 11:55:34 +1000 Subject: [PATCH 01/23] Add peekable_iterator --- library/core/src/iter/mod.rs | 2 ++ library/core/src/iter/traits/mod.rs | 3 ++ library/core/src/iter/traits/peekable.rs | 42 ++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 library/core/src/iter/traits/peekable.rs diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index d532f1e568071..b2fded2400d7b 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -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")] diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs index b330e9ffe21ac..116353c9a1783 100644 --- a/library/core/src/iter/traits/mod.rs +++ b/library/core/src/iter/traits/mod.rs @@ -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")] @@ -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::{ diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs new file mode 100644 index 0000000000000..99aeb0c87d6f7 --- /dev/null +++ b/library/core/src/iter/traits/peekable.rs @@ -0,0 +1,42 @@ +#[unstable(feature = "peekable_iterator", issue = "132973")] +/// Iterators which inherently support `peek()` without needing to be wrapped by a `Peekable`. +pub trait PeekableIterator: Iterator { + /// returns a reference to the `next()` value without advancing the iterator. + /// Just like `next()`, if there is a value, it returns a reference to it in Some() + /// if the iteration is finished, a `None` is returned + /// + /// # Examples + /// Basic usage: + /// ``` + /// let xs = [1, 2, 3]; + /// let mut iter = xs.iter(); + /// + /// // peek() allows us to check the future value + /// assert_eq!(iter.peek(), Some(&&1)); + /// assert_eq!(iter.next(), Some(&1)); + /// + /// // peek() doesn't move the iterator forward + /// assert_eq!(iter.peek(), Some(&&2)); + /// assert_eq!(iter.peek(), Some(&&2)); + /// + /// ``` + fn peek(&mut self) -> Option<&Self::Item>; + + /// returns the `next()` element if a predicate holds true + fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option { + let Some(item) = self.peek() else { + return None; + }; + + if func(item) { self.next() } else { None } + } + + /// move forward and return the `next()` item if it is equal to the expected value + fn next_if_eq(&mut self, expected: &T) -> Option + where + Self::Item: PartialEq, + T: ?Sized, + { + self.next_if(|x| x == expected) + } +} From 1a92c375d5e3c10ff3deb1f050e364d4e38b3c51 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Tue, 5 Aug 2025 13:45:58 +1000 Subject: [PATCH 02/23] Implement peekable for slice::iter --- library/core/src/slice/iter.rs | 3 ++- library/core/src/slice/iter/macros.rs | 28 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index ac096afb38af0..7c62170ac4b49 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -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}; diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 236bdf9d89cae..1f1fa250775fd 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -491,6 +491,34 @@ macro_rules! iterator { } } + #[unstable(feature = "peekable_iterator", issue = "132973")] + impl<'a, T> PeekableIterator for $name<'a, T> { + fn peek(&mut self) -> Option<&$elem> { + 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 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>(end_or_len) { + return None; + } + } + // SAFETY: Now that we know it wasn't empty and we've moved past + // the first one (to avoid giving a duplicate `&mut` next time), + // we can give out a reference to it. + Some(core::mem::transmute(&self.ptr)) + } + } + } #[stable(feature = "default_iters", since = "1.70.0")] impl Default for $name<'_, T> { /// Creates an empty slice iterator. From 332bddfb892d3cf9a0587f488884044aae404b2c Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Tue, 5 Aug 2025 18:23:41 +1000 Subject: [PATCH 03/23] Implement new PeekableIterator for Iter --- library/core/src/iter/traits/peekable.rs | 33 ++++++------------------ library/core/src/slice/iter/macros.rs | 12 ++++----- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index 99aeb0c87d6f7..99562b2ca2946 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -1,34 +1,17 @@ #[unstable(feature = "peekable_iterator", issue = "132973")] /// Iterators which inherently support `peek()` without needing to be wrapped by a `Peekable`. pub trait PeekableIterator: Iterator { - /// returns a reference to the `next()` value without advancing the iterator. - /// Just like `next()`, if there is a value, it returns a reference to it in Some() - /// if the iteration is finished, a `None` is returned - /// - /// # Examples - /// Basic usage: - /// ``` - /// let xs = [1, 2, 3]; - /// let mut iter = xs.iter(); - /// - /// // peek() allows us to check the future value - /// assert_eq!(iter.peek(), Some(&&1)); - /// assert_eq!(iter.next(), Some(&1)); - /// - /// // peek() doesn't move the iterator forward - /// assert_eq!(iter.peek(), Some(&&2)); - /// assert_eq!(iter.peek(), Some(&&2)); - /// - /// ``` - fn peek(&mut self) -> Option<&Self::Item>; + /// executes the closure with an Option containing `None` if the iterator is exhausted or Some(&Self::Item) + fn peek_with(&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(&mut self, func: impl for<'a> FnOnce(&'a Self::Item) -> T) -> Option { + self.peek_with(|x| x.map(|y| func(y))) + } /// returns the `next()` element if a predicate holds true fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option { - let Some(item) = self.peek() else { - return None; - }; - - if func(item) { self.next() } else { None } + self.peek_map(func).and_then(|_| self.next()) } /// move forward and return the `next()` item if it is equal to the expected value diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 1f1fa250775fd..ab84ef4bbe48f 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -493,7 +493,7 @@ macro_rules! iterator { #[unstable(feature = "peekable_iterator", issue = "132973")] impl<'a, T> PeekableIterator for $name<'a, T> { - fn peek(&mut self) -> Option<&$elem> { + fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> T) -> T { let ptr = self.ptr; let end_or_len = self.end_or_len; @@ -502,20 +502,20 @@ macro_rules! iterator { if T::IS_ZST { let len = end_or_len.addr(); if len == 0 { - return None; + 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>(end_or_len) { - return None; + return func(None); } } - // SAFETY: Now that we know it wasn't empty and we've moved past - // the first one (to avoid giving a duplicate `&mut` next time), + // SAFETY: Now that we know it wasn't empty // we can give out a reference to it. - Some(core::mem::transmute(&self.ptr)) + let tmp = {ptr}.$into_ref(); + func(Some(&tmp)) } } } From e049298e2ef70a4ed788fef5bc7b69493cac87d5 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Tue, 5 Aug 2025 18:37:47 +1000 Subject: [PATCH 04/23] Replace T with U --- library/core/src/slice/iter/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index ab84ef4bbe48f..a7668f93da049 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -493,7 +493,7 @@ macro_rules! iterator { #[unstable(feature = "peekable_iterator", issue = "132973")] impl<'a, T> PeekableIterator for $name<'a, T> { - fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> T) -> T { + fn peek_with(&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; From 7eb6bbfe1bbcbcf66987159b22283454e750879f Mon Sep 17 00:00:00 2001 From: Waleed Dahshan <58462210+wmstack@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:52:12 +1000 Subject: [PATCH 05/23] Apply suggestions from code review Improve documentation for `PeekableIterator` to reflect the changes in API Co-authored-by: +merlan #flirora --- library/core/src/iter/traits/peekable.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index 99562b2ca2946..c9ebf88fda05d 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -1,20 +1,20 @@ #[unstable(feature = "peekable_iterator", issue = "132973")] -/// Iterators which inherently support `peek()` without needing to be wrapped by a `Peekable`. +/// Iterators which inherently support peeking without needing to be wrapped by a `Peekable`. pub trait PeekableIterator: Iterator { - /// executes the closure with an Option containing `None` if the iterator is exhausted or Some(&Self::Item) + /// Executes the closure with a reference to the `next()` value without advancing the iterator. fn peek_with(&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. + /// Executes the closure on the `next()` element without advancing the iterator, or returns `None` if the iterator is exhausted. fn peek_map(&mut self, func: impl for<'a> FnOnce(&'a Self::Item) -> T) -> Option { self.peek_with(|x| x.map(|y| func(y))) } - /// returns the `next()` element if a predicate holds true + /// Returns the `next()` element if the given predicate holds true. fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option { self.peek_map(func).and_then(|_| self.next()) } - /// move forward and return the `next()` item if it is equal to the expected value + /// Moves forward and return the `next()` item if it is equal to the expected value. fn next_if_eq(&mut self, expected: &T) -> Option where Self::Item: PartialEq, From 26dacee73cf65a18469efa15053d09f70ced26e7 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Wed, 6 Aug 2025 13:04:11 +1000 Subject: [PATCH 06/23] Update next_if to use the bool --- library/core/src/iter/traits/peekable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index c9ebf88fda05d..00d161e497f47 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -11,7 +11,7 @@ pub trait PeekableIterator: Iterator { /// Returns the `next()` element if the given predicate holds true. fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option { - self.peek_map(func).and_then(|_| self.next()) + self.peek_with(|x| if func(x) { self.next() } else { None }) } /// Moves forward and return the `next()` item if it is equal to the expected value. From 8b0efa3db22e41f962456baf5b90f07cf3d9508c Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Wed, 6 Aug 2025 13:15:37 +1000 Subject: [PATCH 07/23] Update next_if logic to handle Some(false) correctly --- library/core/src/iter/traits/peekable.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index 00d161e497f47..0de6d98122630 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -11,7 +11,10 @@ pub trait PeekableIterator: Iterator { /// Returns the `next()` element if the given predicate holds true. fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option { - self.peek_with(|x| if func(x) { self.next() } else { None }) + 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. From 9b9ab457e8884be6f3315eb7f253b14b9c492b47 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Wed, 6 Aug 2025 13:34:42 +1000 Subject: [PATCH 08/23] Implement PeekableIterator for Chars --- library/core/src/str/iter.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index d2985d8a18669..6b65b9c9f5f51 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -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; @@ -132,6 +132,18 @@ impl<'a> DoubleEndedIterator for Chars<'a> { } } +#[unstable(feature = "peekable_iterator", issue = "132973")] +impl<'a> PeekableIterator for Chars<'a> { + fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> T) -> T { + // SAFETY: `str` invariant says `self.iter` is a valid UTF-8 string and + // the resulting `ch` is a valid Unicode Scalar Value. + let tmp = unsafe { + next_code_point(&mut self.iter.clone()).map(|ch| char::from_u32_unchecked(ch)) + }; + func(&tmp) + } +} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Chars<'_> {} From f18ad01733effee23bb453f22f65f390651da8ee Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Wed, 6 Aug 2025 13:56:36 +1000 Subject: [PATCH 09/23] Peek by cloning on Chars --- library/core/src/str/iter.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 6b65b9c9f5f51..1baa17f644f4e 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -135,11 +135,7 @@ impl<'a> DoubleEndedIterator for Chars<'a> { #[unstable(feature = "peekable_iterator", issue = "132973")] impl<'a> PeekableIterator for Chars<'a> { fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> T) -> T { - // SAFETY: `str` invariant says `self.iter` is a valid UTF-8 string and - // the resulting `ch` is a valid Unicode Scalar Value. - let tmp = unsafe { - next_code_point(&mut self.iter.clone()).map(|ch| char::from_u32_unchecked(ch)) - }; + let tmp = self.clone().next(); func(&tmp) } } From 74ad80c9451b1166bdc15dc53278b40ecb6fc29a Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Wed, 6 Aug 2025 14:45:37 +1000 Subject: [PATCH 10/23] fix input parameter in Chars --- library/core/src/str/iter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 1baa17f644f4e..4bb2368eec7a2 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -136,7 +136,7 @@ impl<'a> DoubleEndedIterator for Chars<'a> { impl<'a> PeekableIterator for Chars<'a> { fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> T) -> T { let tmp = self.clone().next(); - func(&tmp) + func(tmp.as_ref()) } } From 10d9290bda890d60614b69132a6fa0b6af38f3ec Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Wed, 6 Aug 2025 16:35:35 +1000 Subject: [PATCH 11/23] Implement PeekableIterator for IntoIter --- library/core/src/array/iter.rs | 9 ++++++++- library/core/src/array/iter/iter_inner.rs | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index cd2a9e00b5a6e..4faf152f9e11b 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -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::{ManuallyDrop, MaybeUninit}; use crate::num::NonZero; use crate::ops::{Deref as _, DerefMut as _, IndexRange, Range, Try}; @@ -359,6 +359,13 @@ impl FusedIterator for IntoIter {} #[stable(feature = "array_value_iter_impls", since = "1.40.0")] unsafe impl TrustedLen for IntoIter {} +#[unstable(feature = "peekable_iterator", issue = "132973")] +impl PeekableIterator for IntoIter { + fn peek_with(&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] diff --git a/library/core/src/array/iter/iter_inner.rs b/library/core/src/array/iter/iter_inner.rs index 3c2343591f8cf..7a623a257e300 100644 --- a/library/core/src/array/iter/iter_inner.rs +++ b/library/core/src/array/iter/iter_inner.rs @@ -175,6 +175,16 @@ impl PolymorphicIter<[MaybeUninit]> { }) } + #[inline] + pub(super) fn peek_with(&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) { let len = self.len(); From a279e8c264e754307b0187ed7b212f00bc96bdb9 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Thu, 7 Aug 2025 10:09:02 +1000 Subject: [PATCH 12/23] Forget temporary in iter_inner --- library/core/src/array/iter/iter_inner.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/src/array/iter/iter_inner.rs b/library/core/src/array/iter/iter_inner.rs index 7a623a257e300..769be779a139d 100644 --- a/library/core/src/array/iter/iter_inner.rs +++ b/library/core/src/array/iter/iter_inner.rs @@ -176,13 +176,16 @@ impl PolymorphicIter<[MaybeUninit]> { } #[inline] - pub(super) fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b T>) -> U) -> U { + pub(super) fn peek_with(&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()) + let out = func(tmp.as_ref()); + // Avoid dropping before the item is consumed + crate::mem::forget(tmp); + out } #[inline] From 750d963d2cac0fefaf51432641d05b3374ac5876 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Thu, 7 Aug 2025 10:09:51 +1000 Subject: [PATCH 13/23] Implement PeekableIterator for Peekable --- library/core/src/iter/adapters/peekable.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index 9f6d1df57dbe8..b05b1d2847589 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -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 @@ -461,6 +461,14 @@ impl Peekable { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Peekable where I: TrustedLen {} +#[unstable(feature = "peekable_iterator", issue = "132973")] +impl PeekableIterator for Peekable { + fn peek_with(&mut self, func: impl for<'a> FnOnce(Option<&'a Self::Item>) -> T) -> T { + let tmp = self.peek(); + func(tmp.as_ref()) + } +} + #[unstable(issue = "none", feature = "inplace_iteration")] unsafe impl SourceIter for Peekable where From 8243b790293b81066f1190e9a3b0daedd245b5e1 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Thu, 7 Aug 2025 10:14:12 +1000 Subject: [PATCH 14/23] Remove as_ref() --- library/core/src/iter/adapters/peekable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index b05b1d2847589..a0b4a4b6d6657 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -465,7 +465,7 @@ unsafe impl TrustedLen for Peekable where I: TrustedLen {} impl PeekableIterator for Peekable { fn peek_with(&mut self, func: impl for<'a> FnOnce(Option<&'a Self::Item>) -> T) -> T { let tmp = self.peek(); - func(tmp.as_ref()) + func(tmp) } } From 25fabb4be360cd4315ad2f7e3b6e076a4555ef44 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan <58462210+wmstack@users.noreply.github.com> Date: Sat, 9 Aug 2025 17:55:17 +1000 Subject: [PATCH 15/23] Remove unneeded transmute Co-authored-by: Tim (Theemathas) Chirananthavat --- library/core/src/slice/iter/macros.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index a7668f93da049..87a44792dde92 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -505,16 +505,13 @@ macro_rules! iterator { 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>(end_or_len) { + if ptr.as_ptr() == 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(); + let tmp = ptr.$into_ref(); func(Some(&tmp)) } } From cd8d97cfe0393f012b813f7cb666e052a36a705d Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Sat, 9 Aug 2025 17:58:04 +1000 Subject: [PATCH 16/23] Use assume_init_ref directly from slice --- library/core/src/array/iter/iter_inner.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/library/core/src/array/iter/iter_inner.rs b/library/core/src/array/iter/iter_inner.rs index 769be779a139d..4706b8e64f107 100644 --- a/library/core/src/array/iter/iter_inner.rs +++ b/library/core/src/array/iter/iter_inner.rs @@ -177,15 +177,10 @@ impl PolymorphicIter<[MaybeUninit]> { #[inline] pub(super) fn peek_with(&self, func: impl for<'b> FnOnce(Option<&'b T>) -> U) -> U { - let tmp = self.alive.clone().next().map(|idx| { + func(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 + unsafe { self.data.get_unchecked(idx).assume_init_ref() } + })); } #[inline] From 308da3e463b03c0be89129eca3f1d7a2e30ded34 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Sun, 10 Aug 2025 08:28:59 +1000 Subject: [PATCH 17/23] fix peek_with in macros --- library/core/src/array/iter/iter_inner.rs | 2 +- library/core/src/slice/iter/macros.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/library/core/src/array/iter/iter_inner.rs b/library/core/src/array/iter/iter_inner.rs index 4706b8e64f107..125e1daca7321 100644 --- a/library/core/src/array/iter/iter_inner.rs +++ b/library/core/src/array/iter/iter_inner.rs @@ -180,7 +180,7 @@ impl PolymorphicIter<[MaybeUninit]> { func(self.alive.clone().next().map(|idx| { // SAFETY: `idx` is in self.alive range unsafe { self.data.get_unchecked(idx).assume_init_ref() } - })); + })) } #[inline] diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 87a44792dde92..a2b6c101d4ae7 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -494,7 +494,6 @@ macro_rules! iterator { #[unstable(feature = "peekable_iterator", issue = "132973")] impl<'a, T> PeekableIterator for $name<'a, T> { fn peek_with(&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. @@ -505,14 +504,13 @@ macro_rules! iterator { return func(None); } } else { - if ptr.as_ptr() == end_or_len { + if self.ptr == crate::intrinsics::transmute::<$ptr, NonNull>(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)) + func(Some(self.ptr.$into_ref()).as_ref()) } } } From 078ecf1c8b88704986385b4e462d323193915106 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Thu, 4 Sep 2025 14:27:30 +1000 Subject: [PATCH 18/23] Reduce unsafe scope with Iter/IterMut --- library/core/src/slice/iter/macros.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index a2b6c101d4ae7..7cc7a45025630 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -494,23 +494,15 @@ macro_rules! iterator { #[unstable(feature = "peekable_iterator", issue = "132973")] impl<'a, T> PeekableIterator for $name<'a, T> { fn peek_with(&mut self, func: impl for<'b> FnOnce(Option<&'b Self::Item>) -> U) -> U { - 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 { - if self.ptr == crate::intrinsics::transmute::<$ptr, NonNull>(end_or_len) { - return func(None); - } - } - // SAFETY: Now that we know it wasn't empty - // we can give out a reference to it. - func(Some(self.ptr.$into_ref()).as_ref()) + if len!(self) == 0 { + func(None) + } else { + // SAFETY: element within bounds as len > 0 + // Reference is dropped after the closure completes + // and can not outlive the mutable borrow of self + let tmp = unsafe { & $( $mut_ )? *self.ptr.as_ptr() }; + func(Some(tmp).as_ref()) } } } From 3ceb54024ade7cedc592c806c02f786601fbfec0 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Thu, 4 Sep 2025 17:01:20 +1000 Subject: [PATCH 19/23] remove peek_map, add examples --- library/core/src/iter/traits/peekable.rs | 61 +++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index 0de6d98122630..6e27d8421bb4d 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -2,16 +2,65 @@ /// 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. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(peekable_iterator)] + /// use std::iter::PeekableIterator; + /// + /// let mut vals = [0, 1, 2].into_iter(); + /// + /// assert_eq!(vals.peek_with(|x| x.copied()), Some(0)); + /// // element is not consumed + /// assert_eq!(vals.next(), Some(0)); + /// + /// // examine the pending element + /// assert_eq!(vals.peek_with(|x| x), Some(&1)); + /// assert_eq!(vals.next(), Some(1)); + /// + /// // determine if the iterator has an element without advancing + /// assert_eq!(vals.peek_with(|x| x.is_some()), false); + /// assert_eq!(vals.next(), Some(2)); + /// + /// // exhausted iterator + /// assert_eq!(vals.next(), None); + /// assert_eq!(vals.peek_with(|x| x), None); + /// ``` fn peek_with(&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(&mut self, func: impl for<'a> FnOnce(&'a Self::Item) -> T) -> Option { - self.peek_with(|x| x.map(|y| func(y))) - } - /// Returns the `next()` element if the given predicate holds true. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(peekable_iterator)] + /// use std::iter::PeekableIterator; + /// fn parse_number(s: &str) -> u32 { + /// let mut c = s.chars(); + /// + /// let base = if c.next_if_eq(&"0").is_some() { + /// match c.next_if(|c| "oxb".contains(c)) { + /// Some("x") => 16, + /// Some("b") => 2, + /// _ => 8 + /// } + /// } else { + /// 10 + /// } + /// + /// u32::from_str_radix(c.as_str(), base).unwrap() + /// } + /// + /// assert_eq!(parse_number("055"), 45); + /// assert_eq!(parse_number("0o42"), 34); + /// assert_eq!(parse_number("0x11"), 17); + /// ``` + /// fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option { - match self.peek_map(func) { + match self.peek_with(|x| x.map(|y| func(y))) { Some(true) => self.next(), _ => None, } From 512e44f0d358c875ec3e34c8c473c8332c619a69 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Thu, 4 Sep 2025 17:14:23 +1000 Subject: [PATCH 20/23] Fix trailing whitespace, punctuation --- library/core/src/iter/traits/peekable.rs | 6 +++--- library/core/src/slice/iter/macros.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index 6e27d8421bb4d..470babac92283 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -17,7 +17,7 @@ pub trait PeekableIterator: Iterator { /// assert_eq!(vals.next(), Some(0)); /// /// // examine the pending element - /// assert_eq!(vals.peek_with(|x| x), Some(&1)); + /// assert_eq!(vals.peek_with(|x| x.copied()), Some(1)); /// assert_eq!(vals.next(), Some(1)); /// /// // determine if the iterator has an element without advancing @@ -26,7 +26,7 @@ pub trait PeekableIterator: Iterator { /// /// // exhausted iterator /// assert_eq!(vals.next(), None); - /// assert_eq!(vals.peek_with(|x| x), None); + /// assert_eq!(vals.peek_with(|x| x.copied()), None); /// ``` fn peek_with(&mut self, func: impl for<'a> FnOnce(Option<&'a Self::Item>) -> T) -> T; @@ -50,7 +50,7 @@ pub trait PeekableIterator: Iterator { /// } else { /// 10 /// } - /// + /// /// u32::from_str_radix(c.as_str(), base).unwrap() /// } /// diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 7cc7a45025630..6c903af079a51 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -498,9 +498,9 @@ macro_rules! iterator { if len!(self) == 0 { func(None) } else { - // SAFETY: element within bounds as len > 0 - // Reference is dropped after the closure completes - // and can not outlive the mutable borrow of self + // SAFETY: Element within bounds as len > 0. + // The reference is dropped after the closure completes + // and can not outlive the mutable borrow of self. let tmp = unsafe { & $( $mut_ )? *self.ptr.as_ptr() }; func(Some(tmp).as_ref()) } From 547d7ac27768350a10272795065bc0b84550ed50 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Sun, 15 Feb 2026 18:44:12 +1100 Subject: [PATCH 21/23] fix example chars --- library/core/src/iter/traits/peekable.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index 470babac92283..3291636c8e9be 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -41,10 +41,10 @@ pub trait PeekableIterator: Iterator { /// fn parse_number(s: &str) -> u32 { /// let mut c = s.chars(); /// - /// let base = if c.next_if_eq(&"0").is_some() { + /// let base = if c.next_if_eq(&'0').is_some() { /// match c.next_if(|c| "oxb".contains(c)) { - /// Some("x") => 16, - /// Some("b") => 2, + /// Some('x') => 16, + /// Some('b') => 2, /// _ => 8 /// } /// } else { From 2f1e47b2062dae3a54fa4a8788ba368a2cf05af9 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Sun, 15 Feb 2026 18:47:44 +1100 Subject: [PATCH 22/23] add 0 case --- library/core/src/iter/traits/peekable.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index 3291636c8e9be..62168368cefa1 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -39,6 +39,10 @@ pub trait PeekableIterator: Iterator { /// #![feature(peekable_iterator)] /// use std::iter::PeekableIterator; /// fn parse_number(s: &str) -> u32 { + /// if s == "0" { + /// return 0 + /// } + /// /// let mut c = s.chars(); /// /// let base = if c.next_if_eq(&'0').is_some() { @@ -57,6 +61,7 @@ pub trait PeekableIterator: Iterator { /// assert_eq!(parse_number("055"), 45); /// assert_eq!(parse_number("0o42"), 34); /// assert_eq!(parse_number("0x11"), 17); + /// assert_eq!(parse_number("0"), 0); /// ``` /// fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option { From 22a1c0f6b36fb55b2adc9bb3d9d16923d1743bb8 Mon Sep 17 00:00:00 2001 From: Waleed Dahshan Date: Thu, 19 Feb 2026 08:11:25 +1100 Subject: [PATCH 23/23] fix example syntax --- library/core/src/iter/traits/peekable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/iter/traits/peekable.rs b/library/core/src/iter/traits/peekable.rs index 62168368cefa1..77d8dcdda6d80 100644 --- a/library/core/src/iter/traits/peekable.rs +++ b/library/core/src/iter/traits/peekable.rs @@ -46,14 +46,14 @@ pub trait PeekableIterator: Iterator { /// let mut c = s.chars(); /// /// let base = if c.next_if_eq(&'0').is_some() { - /// match c.next_if(|c| "oxb".contains(c)) { + /// match c.next_if(|c| "oxb".contains(*c)) { /// Some('x') => 16, /// Some('b') => 2, /// _ => 8 /// } /// } else { /// 10 - /// } + /// }; /// /// u32::from_str_radix(c.as_str(), base).unwrap() /// }