Skip to content

Conversation

wmstack
Copy link
Contributor

@wmstack wmstack commented Aug 5, 2025

Tracking issue: #132973

Implementation

pub trait PeekableIterator: Iterator {
  // required method
  fn peek_with<T>(&mut self, func: impl for<'a> FnOnce(Option<&'a Self::Item>) -> T) -> T

  // provided methods
  fn peek_map<T>(&mut self, func: impl for<'a> FnOnce(&'a Self::Item) -> T) -> Option<T>
  fn next_if(&mut self, func: impl FnOnce(&Self::Item) -> bool) -> Option<Self::Item>
  fn next_if_eq<T>(&mut self, expected: &T) -> Option<Self::Item>
  where
    Self::Item: PartialEq<T>,
    T: ?Sized,
}

Todo: Determine which structures need to implement PeekableIterator:

  • slice::iter
  • str::Chars
  • vec::IntoIter
  • Peekable<T>

Unresolved Issues:

  • Add another trait for iterators that can peek() without mutating?

@rustbot
Copy link
Collaborator

rustbot commented Aug 5, 2025

r? @tgross35

rustbot has assigned @tgross35.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Aug 5, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@theemathas
Copy link
Contributor

Maybe the correct function signature is this?

fn peek<T>(&mut self, callback: impl for<'a> FnOnce(Option<&'a Self::Item>) -> T) -> T

This would mean, for example, that str::Chars would be able to implement this without having a field inside the iterator that contains a char. Instead, it would be able to create a local variable in the peek method that stores the char.

@rust-log-analyzer

This comment has been minimized.

wmstack and others added 4 commits August 6, 2025 12:52
Improve documentation for `PeekableIterator` to reflect the changes in API

Co-authored-by: +merlan #flirora <flirora@flirora.xyz>
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

Comment on lines 485 to 488
// 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.

wmstack and others added 2 commits August 9, 2025 17:55
Co-authored-by: Tim (Theemathas) Chirananthavat <theemathas@gmail.com>
@rust-log-analyzer

This comment has been minimized.

Copy link
Contributor

@tgross35 tgross35 left a comment

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 be tested at all. Please make sure that even unstable API gets helpful examples, aside from checking the implementation they serve as a very useful smoke test for whether or not the API makes sense in the first place.

Additionally, the type-specific impls need tests in library/coretests

View changes since this review

Comment on lines 7 to 10
/// 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?

Comment on lines 476 to 491
// 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<T>>(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())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Few requests:

  1. Use mem::transmute rather than the intrinsic directly
  2. Use NonNull methods if possible. If not, add a comment about why this is
  3. Can this unsafe block be broken up?

Copy link
Contributor

Choose a reason for hiding this comment

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

#144935 (comment) actually covers this better

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 3, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 4, 2025

⚠️ Warning ⚠️

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer
Copy link
Collaborator

The job pr-check-2 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
   |
18 |     }
   |      ^ help: add `;` here
19 |
20 |     u32::from_str_radix(c.as_str(), base).unwrap()
   |     --- unexpected token

error[E0308]: mismatched types
  --> library/core/src/iter/traits/peekable.rs:45:32
   |
10 |     let base = if c.next_if_eq(&"0").is_some() {
   |                     ---------- ^^^^ expected `&char`, found `&&str`
   |                     |
   |                     arguments to this method are incorrect
   |
   = note: expected reference `&char`
              found reference `&&'static str`
note: method defined here
  --> /checkout/library/core/src/iter/traits/peekable.rs:70:8
   |
70 |     fn next_if_eq<T>(&mut self, expected: &T) -> Option<Self::Item>
   |        ^^^^^^^^^^

error[E0277]: the trait bound `&char: Pattern` is not satisfied
    --> library/core/src/iter/traits/peekable.rs:46:44
     |
  11 |         match c.next_if(|c| "oxb".contains(c)) {
     |                                   -------- ^ the trait `Fn(char)` is not implemented for `char`
     |                                   |
     |                                   required by a bound introduced by this call
     |
     = note: required for `&char` to implement `FnOnce(char)`
     = note: required for `&char` to implement `Pattern`
note: required by a bound in `core::str::<impl str>::contains`
    --> /checkout/library/core/src/str/mod.rs:1340:24
     |
1340 |     pub fn contains<P: Pattern>(&self, pat: P) -> bool {
     |                        ^^^^^^^ required by this bound in `core::str::<impl str>::contains`
help: consider dereferencing here
     |
  11 |         match c.next_if(|c| "oxb".contains(*c)) {
     |                                            +

error[E0308]: mismatched types
  --> library/core/src/iter/traits/peekable.rs:47:18
   |
11 |         match c.next_if(|c| "oxb".contains(c)) {
   |               -------------------------------- this expression has type `Option<char>`
12 |             Some("x") => 16,
   |                  ^^^ expected `char`, found `&str`
   |
help: if you meant to write a `char` literal, use single quotes
   |
12 -             Some("x") => 16,
12 +             Some('x') => 16,
   |

error[E0308]: mismatched types
  --> library/core/src/iter/traits/peekable.rs:48:18
   |
11 |         match c.next_if(|c| "oxb".contains(c)) {
   |               -------------------------------- this expression has type `Option<char>`
12 |             Some("x") => 16,
13 |             Some("b") => 2,
   |                  ^^^ expected `char`, found `&str`
   |
help: if you meant to write a `char` literal, use single quotes
   |
13 -             Some("b") => 2,
13 +             Some('b') => 2,
   |

error: aborting due to 5 previous errors

Some errors have detailed explanations: E0277, E0308.
---
   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::panicking::assert_failed_inner
   3: core::panicking::assert_failed
   4: rust_out::main::_doctest_main_library_core_src_iter_traits_peekable_rs_9_0
   5: rust_out::main
   6: core::ops::function::FnOnce::call_once
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

---- library/core/src/iter/traits/peekable.rs - iter::traits::peekable::PeekableIterator::peek_with (line 9) stdout end ----

@bluebear94
Copy link
Contributor

@wmstack Are you still open to working on this PR? If not, then I’m willing to take over the work on this.

@wmstack
Copy link
Contributor Author

wmstack commented Oct 9, 2025

@bluebear94 Feel free to pick this up as I’m tied up with coursework at the moment.

@bors
Copy link
Collaborator

bors commented Oct 14, 2025

☔ The latest upstream changes (presumably #147353) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants