Skip to content
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

Add Peekable::next_if #72310

Merged
merged 1 commit into from
May 29, 2020
Merged
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
63 changes: 63 additions & 0 deletions src/libcore/iter/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1574,6 +1574,69 @@ impl<I: Iterator> Peekable<I> {
let iter = &mut self.iter;
self.peeked.get_or_insert_with(|| iter.next()).as_ref()
}

/// Consume the next value of this iterator if a condition is true.
Copy link
Contributor

@pickfire pickfire Sep 2, 2020

Choose a reason for hiding this comment

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

I believe Consume and return or Take may be a better choice?

///
/// If `func` returns `true` for the next value of this iterator, consume and return it.
/// Otherwise, return `None`.
///
/// # Examples
/// Consume a number if it's equal to 0.
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this description duplicated from the comments in the example?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the same description, but using a different implementation.

/// ```
/// #![feature(peekable_next_if)]
/// let mut iter = (0..5).peekable();
/// // The first item of the iterator is 0; consume it.
/// assert_eq!(iter.next_if(|&x| x == 0), Some(0));
/// // The next item returned is now 1, so `consume` will return `false`.
/// assert_eq!(iter.next_if(|&x| x == 0), None);
/// // `next_if` saves the value of the next item if it was not equal to `expected`.
/// assert_eq!(iter.next(), Some(1));
/// ```
///
/// Consume any number less than 10.
/// ```
/// #![feature(peekable_next_if)]
/// let mut iter = (1..20).peekable();
/// // Consume all numbers less than 10
/// while iter.next_if(|&x| x < 10).is_some() {}
/// // The next value returned will be 10
/// assert_eq!(iter.next(), Some(10));
/// ```
#[unstable(feature = "peekable_next_if", issue = "72480")]
pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
match self.next() {
Some(matched) if func(&matched) => Some(matched),
other => {
// Since we called `self.next()`, we consumed `self.peeked`.
assert!(self.peeked.is_none());
self.peeked = Some(other);
None
}
}
}

/// Consume the next item if it is equal to `expected`.
///
/// # Example
/// Consume a number if it's equal to 0.
/// ```
/// #![feature(peekable_next_if)]
/// let mut iter = (0..5).peekable();
/// // The first item of the iterator is 0; consume it.
/// assert_eq!(iter.next_if_eq(&0), Some(0));
/// // The next item returned is now 1, so `consume` will return `false`.
/// assert_eq!(iter.next_if_eq(&0), None);
/// // `next_if_eq` saves the value of the next item if it was not equal to `expected`.
/// assert_eq!(iter.next(), Some(1));
/// ```
#[unstable(feature = "peekable_next_if", issue = "72480")]
pub fn next_if_eq<R>(&mut self, expected: &R) -> Option<I::Item>
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason why this is R? Why not T?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think R for reference? I don't remember any more, I'll switch it to T.

where
R: ?Sized,
I::Item: PartialEq<R>,
{
self.next_if(|next| next == expected)
}
}

/// An iterator that rejects elements while `predicate` returns `true`.
Expand Down
24 changes: 24 additions & 0 deletions src/libcore/tests/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,30 @@ fn test_iterator_peekable_rfold() {
assert_eq!(i, xs.len());
}

#[test]
fn test_iterator_peekable_next_if_eq() {
// first, try on references
let xs = vec!["Heart", "of", "Gold"];
let mut it = xs.into_iter().peekable();
// try before `peek()`
assert_eq!(it.next_if_eq(&"trillian"), None);
assert_eq!(it.next_if_eq(&"Heart"), Some("Heart"));
// try after peek()
assert_eq!(it.peek(), Some(&"of"));
assert_eq!(it.next_if_eq(&"of"), Some("of"));
assert_eq!(it.next_if_eq(&"zaphod"), None);
// make sure `next()` still behaves
assert_eq!(it.next(), Some("Gold"));

// make sure comparison works for owned values
let xs = vec![String::from("Ludicrous"), "speed".into()];
let mut it = xs.into_iter().peekable();
// make sure basic functionality works
assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into()));
assert_eq!(it.next_if_eq("speed"), Some("speed".into()));
assert_eq!(it.next_if_eq(""), None);
}

/// This is an iterator that follows the Iterator contract,
/// but it is not fused. After having returned None once, it will start
/// producing elements if .next() is called again.
Expand Down
1 change: 1 addition & 0 deletions src/libcore/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#![feature(leading_trailing_ones)]
#![feature(const_forget)]
#![feature(option_unwrap_none)]
#![feature(peekable_next_if)]

extern crate test;

Expand Down