diff options
| author | Dylan DPC <dylan.dpc@gmail.com> | 2020-05-29 20:21:11 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-05-29 20:21:11 +0200 |
| commit | cbcc4c4f05cef62d283d7205bd00ce7d7adcfec6 (patch) | |
| tree | b1c51c39322fe56b97e3151b9ecd0502d8cf5b68 /src/libcore/iter/adapters | |
| parent | 96dd4690c3aa70ec312448c3f2d50e6dc6fb87df (diff) | |
| parent | 822ad87325d6436a6aea5ae61ca34d9ad45dd839 (diff) | |
| download | rust-cbcc4c4f05cef62d283d7205bd00ce7d7adcfec6.tar.gz rust-cbcc4c4f05cef62d283d7205bd00ce7d7adcfec6.zip | |
Rollup merge of #72310 - jyn514:peekable-next-if, r=dtolnay
Add Peekable::next_if Prior art: `rust_analyzer` uses [`Parser::eat`](https://github.com/rust-analyzer/rust-analyzer/blob/50f4ae798b7c54d417ee88455b87fd0477473150/crates/ra_parser/src/parser.rs#L94), which is `next_if` specialized to `|y| self.next_if(|x| x == y)`. Basically every other parser I've run into in Rust has an equivalent of `Parser::eat`; see for example - [cranelift](https://github.com/bytecodealliance/wasmtime/blob/94190d57244b26baf36629c88104b0ba516510cf/cranelift/reader/src/parser.rs#L498) - [rcc](https://github.com/jyn514/rcc/blob/a8159c3904a0c950fbba817bf9109023fad69033/src/parse/mod.rs#L231) - [crunch](https://github.com/Kixiron/crunch-lang/blob/8521874fab8a7d62bfa7dea8bd1da94b63e31be8/crates/crunch-parser/src/parser/mod.rs#L213-L241) Possible extensions: A specialization of `next_if` to using `Eq::eq`. The only difficulty here is the naming - maybe `next_if_eq`? Alternatives: - Instead of `func: impl FnOnce(&I::Item) -> bool`, use `func: impl FnOnce(I::Item) -> Option<I::Item>`. This has the advantage that `func` can move the value if necessary, but means that there is no guarantee `func` will return the same value it was given. - Instead of `fn next_if(...) -> Option<I::Item>`, use `fn next_if(...) -> bool`. This makes the common case of `iter.next_if(f).is_some()` easier, but makes the unusual case impossible. Bikeshedding on naming: - `next_if` could be renamed to `consume_if` (to match `eat`, but a little more formally) - `next_if_eq` could be renamed to `consume`. This is more concise but less self-explanatory if you haven't written a lot of parsers. - Both of the above, but with `consume` replaced by `eat`.
Diffstat (limited to 'src/libcore/iter/adapters')
| -rw-r--r-- | src/libcore/iter/adapters/mod.rs | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs index 195847ee98d..00529f0e2d5 100644 --- a/src/libcore/iter/adapters/mod.rs +++ b/src/libcore/iter/adapters/mod.rs @@ -1619,6 +1619,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. + /// + /// 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. + /// ``` + /// #![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> + where + R: ?Sized, + I::Item: PartialEq<R>, + { + self.next_if(|next| next == expected) + } } /// An iterator that rejects elements while `predicate` returns `true`. |
