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

Add Peekable::next_if #72310

merged 1 commit into from
May 29, 2020

Conversation

jyn514
Copy link
Member

@jyn514 jyn514 commented May 18, 2020

Prior art:

rust_analyzer uses Parser::eat, 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

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.

@rust-highfive
Copy link
Collaborator

r? @dtolnay

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 18, 2020
Copy link
Member

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

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

Seems plausible to me. Some remaining steps:

  • Please open a tracking issue and change the stability attrs to unstable.
  • Move both of these into the public impl block containing the peek method. Otherwise the docs come out very confusing with multiple impls and the most commonly relevant method way at the bottom. There should be only 1 impl block containing public methods.
  • Move the Borrow bound to a where clause. It will be more readable in documentation.
  • Add tests in src/libcore/tests/iter.rs exercising the Borrow bound.
  • I think your bound is actually backwards i.e. I would assume the point was to support next_if_eq("...") on a peekable of String.

@jyn514 jyn514 mentioned this pull request May 22, 2020
3 tasks
@jyn514
Copy link
Member Author

jyn514 commented May 22, 2020

I think your bound is actually backwards i.e. I would assume the point was to support next_if_eq("...") on a peekable of String.

Hmm, I really wanted next_if_eq(0) to work, but if I reverse the bound then I have to take &R instead of R which means you have to use next_if_eq(&0). I tried taking R, but then I get

error[E0277]: the trait bound `std::string::String: std::borrow::Borrow<&str>` is not satisfied
   --> src/libcore/../libcore/tests/iter.rs:835:19
    |
835 |     assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into()));
    |                   ^^^^^^^^^^ the trait `std::borrow::Borrow<&str>` is not implemented for `std::string::String`
    |
    = help: the following implementations were found:
              <std::string::String as std::borrow::Borrow<str>>

Then I tried having a common type that they both borrow to, but now rustc can't infer the common type:

error[E0282]: type annotations needed
   --> src/libcore/../libcore/tests/iter.rs:822:19
    |
822 |     assert_eq!(it.next_if_eq("trillian"), None);
    |                   ^^^^^^^^^^ cannot infer type for type parameter `C` declared on the associated function `next_if_eq`

Copy link
Member

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

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

  • Please move the methods to the same impl block that contains peek, which is further down. Otherwise there is still more than one block containing public methods, and peek which is the most important one ends up too far down in the documentation.
  • I think we should remove the use of Borrow here. The method should take an argument of type &R where Item: PartialEq<R>. Add Peekable::next_if #72310 (comment) sounds like fighting the language which is not usually good. That sort of thing would be better addressed by something like discarding ownership which will allow calling next_if_eq(0) without a more elaborate trait bound.

@jyn514
Copy link
Member Author

jyn514 commented May 26, 2020

That sort of thing would be better addressed by something like discarding ownership which will allow calling next_if_eq(0) without a more elaborate trait bound.

That's fair. If we remove the Borrow bound altogether though, then next_if_eq("str") won't work.

@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[section]Starting: Linux mingw-check
##[section]Starting: Initialize job
Agent name: 'Azure Pipelines 5'
Agent machine name: 'fv-az578'
Current agent version: '2.169.0'
##[group]Operating System
16.04.6
LTS
LTS
##[endgroup]
##[group]Virtual Environment
Environment: ubuntu-16.04
Version: 20200517.1
Included Software: https:/actions/virtual-environments/blob/ubuntu16/20200517.1/images/linux/Ubuntu1604-README.md
##[endgroup]
Agent running as: 'vsts'
Prepare build directory.
Set build variables.
Download all required tasks.
Download all required tasks.
Downloading task: Bash (3.163.2)
Checking job knob settings.
   Knob: AgentToolsDirectory = /opt/hostedtoolcache Source: ${AGENT_TOOLSDIRECTORY} 
   Knob: AgentPerflog = /home/vsts/perflog Source: ${VSTS_AGENT_PERFLOG} 
Start tracking orphan processes.
##[section]Finishing: Initialize job
##[section]Starting: Configure Job Name
==============================================================================
---
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/412811e1-1c7b-4fce-9033-47d958ecccc2.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/72310/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https:/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https:/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/72310/merge:refs/remotes/pull/72310/merge
---
 ---> 3adb0605cc65
Step 6/7 : ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
 ---> Using cache
 ---> 28dbc326cb7f
Step 7/7 : ENV SCRIPT python3 ../x.py test src/tools/expand-yaml-anchors &&            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu &&            python3 ../x.py build --stage 0 src/tools/build-manifest &&            python3 ../x.py test --stage 0 src/tools/compiletest &&            python3 ../x.py test src/tools/tidy &&            python3 ../x.py doc --stage 0 src/libstd &&            /scripts/validate-toolstate.sh
 ---> 537a01811900
Successfully built 537a01811900
Successfully tagged rust-ci:latest
Built container sha256:537a018119009dc218456238dec90b5530050db1e2a1e166550c218003f6159d
---
    Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
    Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
    Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
    Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
    Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
    Checking chalk-rust-ir v0.10.0
    Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
    Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
    Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
configure: build.submodules     := False
configure: rust.channel         := nightly
configure: rust.debug-assertions := True
configure: rust.dist-src        := False
configure: build.configure-args := ['--enable-sccache', '--disable-manage-submodu ...
configure: writing `config.toml` in current directory
configure: 
configure: run `python /checkout/x.py --help`
configure: 
---
Hugepagesize:       2048 kB
DirectMap4k:      143296 kB
DirectMap2M:     4050944 kB
DirectMap1G:     5242880 kB
+ python3 ../x.py test src/tools/expand-yaml-anchors
Ensuring the YAML anchors in the GitHub Actions config were expanded
Ensuring the YAML anchors in the GitHub Actions config were expanded
Building stage0 tool expand-yaml-anchors (x86_64-unknown-linux-gnu)
   Compiling unicode-xid v0.2.0
   Compiling syn v1.0.11
   Compiling linked-hash-map v0.5.2
   Compiling lazy_static v1.4.0
   Compiling lazy_static v1.4.0
   Compiling yaml-rust v0.4.3
   Compiling quote v1.0.2
   Compiling thiserror-impl v1.0.5
   Compiling thiserror v1.0.5
   Compiling yaml-merge-keys v0.4.0
   Compiling expand-yaml-anchors v0.1.0 (/checkout/src/tools/expand-yaml-anchors)
Build completed successfully in 0:00:26
+ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu
    Finished dev [unoptimized] target(s) in 0.19s
Checking rustdoc artifacts (x86_64-unknown-linux-gnu -> i686-pc-windows-gnu)
---
    Checking rustc_feature v0.0.0 (/checkout/src/librustc_feature)
    Checking fmt_macros v0.0.0 (/checkout/src/libfmt_macros)
    Checking rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
    Checking rustc_hir v0.0.0 (/checkout/src/librustc_hir)
    Checking rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
    Checking chalk-rust-ir v0.10.0
    Checking rustc_parse v0.0.0 (/checkout/src/librustc_parse)
    Checking rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
    Checking rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
skip untracked path src/llvm-project/ during rustfmt invocations
Diff in /checkout/src/libcore/iter/adapters/mod.rs at line 1672:
     /// assert_eq!(iter.next(), Some(1));
     /// ```
     #[unstable(feature = "peekable_next_if", issue = "72480")]
-    pub fn next_if_eq<R>(
-        expected: R,
-    ) -> Option<I::Item>
-    ) -> Option<I::Item>
+    pub fn next_if_eq<R>(&mut self, expected: R) -> Option<I::Item>
     where
-          R: crate::borrow::Borrow<I::Item>,
-          I::Item: PartialEq,
+        R: crate::borrow::Borrow<I::Item>,
+        I::Item: PartialEq,
     {
         self.next_if(|next| next == expected.borrow())
     }
Running `"/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/rustfmt" "--config-path" "/checkout" "--edition" "2018" "--unstable-features" "--skip-children" "--check" "/checkout/src/libcore/iter/adapters/mod.rs"` failed.
If you're running `tidy`, try again with `--bless`. Or, if you just want to format code, run `./x.py fmt` instead.
Build completed unsuccessfully in 0:00:40
== clock drift check ==
  local time: Tue May 26 13:52:08 UTC 2020
  network time: Tue, 26 May 2020 13:52:08 GMT
  network time: Tue, 26 May 2020 13:52:08 GMT
== end clock drift check ==

##[error]Bash exited with code '1'.
##[section]Finishing: Run build
##[section]Starting: Checkout rust-lang/rust@refs/pull/72310/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/72310/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (3827) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@dtolnay
Copy link
Member

dtolnay commented May 26, 2020

You should try it. String implements PartialEq<str> so I would expect it to work.

@jyn514
Copy link
Member Author

jyn514 commented May 26, 2020

Using I::Item: PartialEq<R> gives

error[E0277]: can't compare `&str` with `str`
   --> src/libcore/../libcore/tests/iter.rs:822:19
    |
822 |     assert_eq!(it.next_if_eq("trillian"), None);
    |                   ^^^^^^^^^^ no implementation for `&str == str`
    |
    = help: the trait `std::cmp::PartialEq<str>` is not implemented for `&str`

I tried using PartialEq<&R> but that says I need explicit lifetimes:

error[E0637]: `&` without an explicit lifetime name cannot be used here
    --> src/libcore/iter/adapters/mod.rs:1635:11
     |
1635 |           &I::Item: PartialEq<&R>,
     |           ^ explicit lifetime name needed here

error[E0637]: `&` without an explicit lifetime name cannot be used here
    --> src/libcore/iter/adapters/mod.rs:1635:31
     |
1635 |           &I::Item: PartialEq<&R>,
     |                               ^ explicit lifetime name needed here

and adding explicit lifetimes doesn't help:

error: lifetime may not live long enough
    --> src/libcore/iter/adapters/mod.rs:1639:29
     |
1630 |     pub fn next_if_eq<'a, R>(
     |                       -- lifetime `'a` defined here
...
1639 |         self.next_if(|next| next == expected)
     |                       ----  ^^^^ requires that `'1` must outlive `'a`
     |                       |
     |                       has type `&'1 <I as iter::traits::iterator::Iterator>::Item`

error: aborting due to previous error

I'm pushing the current implementation now but I'm not sure how to proceed, the errors are confusing me.

@jyn514
Copy link
Member Author

jyn514 commented May 26, 2020

Ah wait this is the same error as before, I need to use &"str" instead.

@rust-highfive
Copy link
Collaborator

The job mingw-check of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[section]Starting: Linux mingw-check
##[section]Starting: Initialize job
Agent name: 'Azure Pipelines 12'
Agent machine name: 'fv-az619'
Current agent version: '2.169.0'
##[group]Operating System
16.04.6
LTS
LTS
##[endgroup]
##[group]Virtual Environment
Environment: ubuntu-16.04
Version: 20200517.1
Included Software: https:/actions/virtual-environments/blob/ubuntu16/20200517.1/images/linux/Ubuntu1604-README.md
##[endgroup]
Agent running as: 'vsts'
Prepare build directory.
Set build variables.
Download all required tasks.
Download all required tasks.
Downloading task: Bash (3.163.2)
Checking job knob settings.
   Knob: AgentToolsDirectory = /opt/hostedtoolcache Source: ${AGENT_TOOLSDIRECTORY} 
   Knob: AgentPerflog = /home/vsts/perflog Source: ${VSTS_AGENT_PERFLOG} 
Start tracking orphan processes.
##[section]Finishing: Initialize job
##[section]Starting: Configure Job Name
==============================================================================
---
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/6b4cbd95-844b-4eb9-a7b0-be93f2ef6b38.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/72310/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https:/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https:/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/72310/merge:refs/remotes/pull/72310/merge
---
 ---> 3adb0605cc65
Step 6/7 : ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
 ---> Using cache
 ---> 28dbc326cb7f
Step 7/7 : ENV SCRIPT python3 ../x.py test src/tools/expand-yaml-anchors &&            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu &&            python3 ../x.py build --stage 0 src/tools/build-manifest &&            python3 ../x.py test --stage 0 src/tools/compiletest &&            python3 ../x.py test src/tools/tidy &&            python3 ../x.py doc --stage 0 src/libstd &&            /scripts/validate-toolstate.sh
 ---> 537a01811900
Successfully built 537a01811900
Successfully tagged rust-ci:latest
Built container sha256:537a018119009dc218456238dec90b5530050db1e2a1e166550c218003f6159d
---
   Compiling std v0.0.0 (/checkout/src/libstd)
error: lifetime may not live long enough
    --> src/libcore/iter/adapters/mod.rs:1684:29
     |
1675 |     pub fn next_if_eq<'a, R>(
...
...
1684 |         self.next_if(|next| next == expected)
     |                       ----  ^^^^ requires that `'1` must outlive `'a`
     |                       has type `&'1 <I as iter::traits::iterator::Iterator>::Item`

   Compiling compiler_builtins v0.1.28
error: aborting due to previous error
---
  local time: Tue May 26 20:12:24 UTC 2020
  network time: Tue, 26 May 2020 20:12:24 GMT
== end clock drift check ==

##[error]Bash exited with code '1'.
##[section]Finishing: Run build
##[section]Starting: Checkout rust-lang/rust@refs/pull/72310/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/72310/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (3511) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

src/libcore/iter/adapters/mod.rs Outdated Show resolved Hide resolved
src/libcore/iter/adapters/mod.rs Outdated Show resolved Hide resolved
src/libcore/tests/iter.rs Outdated Show resolved Hide resolved
Comment on lines 836 to 845
assert_eq!(it.next_if_eq("speed"), Some("speed".into()));
/*
assert_eq!(it.next_if_eq(""), None);
// 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"));
*/
Copy link
Member

Choose a reason for hiding this comment

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

This test needs to be finished.

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 I'm just going to remove the commented-out section, it doesn't test anything that's not tested above.

Copy link
Member

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

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

Thanks, seems good.

I squashed the commits.

@dtolnay
Copy link
Member

dtolnay commented May 27, 2020

@bors r+

@bors
Copy link
Contributor

bors commented May 27, 2020

📌 Commit 822ad87 has been approved by dtolnay

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 27, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 27, 2020
Add Peekable::next_if

Prior art:

`rust_analyzer` uses [`Parser::eat`](https:/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:/bytecodealliance/wasmtime/blob/94190d57244b26baf36629c88104b0ba516510cf/cranelift/reader/src/parser.rs#L498)
- [rcc](https:/jyn514/rcc/blob/a8159c3904a0c950fbba817bf9109023fad69033/src/parse/mod.rs#L231)
- [crunch](https:/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`.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 27, 2020
Add Peekable::next_if

Prior art:

`rust_analyzer` uses [`Parser::eat`](https:/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:/bytecodealliance/wasmtime/blob/94190d57244b26baf36629c88104b0ba516510cf/cranelift/reader/src/parser.rs#L498)
- [rcc](https:/jyn514/rcc/blob/a8159c3904a0c950fbba817bf9109023fad69033/src/parse/mod.rs#L231)
- [crunch](https:/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`.
@dtolnay dtolnay added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label May 28, 2020
RalfJung added a commit to RalfJung/rust that referenced this pull request May 29, 2020
Add Peekable::next_if

Prior art:

`rust_analyzer` uses [`Parser::eat`](https:/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:/bytecodealliance/wasmtime/blob/94190d57244b26baf36629c88104b0ba516510cf/cranelift/reader/src/parser.rs#L498)
- [rcc](https:/jyn514/rcc/blob/a8159c3904a0c950fbba817bf9109023fad69033/src/parse/mod.rs#L231)
- [crunch](https:/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`.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 29, 2020
Add Peekable::next_if

Prior art:

`rust_analyzer` uses [`Parser::eat`](https:/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:/bytecodealliance/wasmtime/blob/94190d57244b26baf36629c88104b0ba516510cf/cranelift/reader/src/parser.rs#L498)
- [rcc](https:/jyn514/rcc/blob/a8159c3904a0c950fbba817bf9109023fad69033/src/parse/mod.rs#L231)
- [crunch](https:/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`.
bors added a commit to rust-lang-ci/rust that referenced this pull request May 29, 2020
Rollup of 9 pull requests

Successful merges:

 - rust-lang#72310 (Add Peekable::next_if)
 - rust-lang#72383 (Suggest using std::mem::drop function instead of explicit destructor call)
 - rust-lang#72398 (SocketAddr and friends now correctly pad its content)
 - rust-lang#72465 (Warn about unused captured variables)
 - rust-lang#72568 (Implement total_cmp for f32, f64)
 - rust-lang#72572 (Add some regression tests)
 - rust-lang#72591 (librustc_middle: Rename upvar_list to closure_captures)
 - rust-lang#72701 (Fix grammar in liballoc raw_vec)
 - rust-lang#72731 (Add missing empty line in E0619 explanation)

Failed merges:

r? @ghost
@bors bors merged commit cbcc4c4 into rust-lang:master May 29, 2020
@jyn514 jyn514 deleted the peekable-next-if branch May 29, 2020 23:53
@apelisse
Copy link

Sorry if this is not the best place to give feedback.

I was actually looking for an iterator when I found the next_if documentation, but I think an iterator would be easier to re-use than the new method, for example:

let mut iter = (1..20).peekable();
while iter.next_if(|&x| x < 10).is_some() {}

Becomes

let mut iter = (1..20).peekable();
for v in iter.while(|&x| x < 10) {
    println!("{}", v);
}

which is, IMO, even more useful when parsing.

@jyn514
Copy link
Member Author

jyn514 commented Aug 11, 2020

Sorry if this is not the best place to give feedback.

The tracking issue would be a better place: #72480

while sounds useful though, are you interested in making a PR?

@apelisse
Copy link

Sure, I can try that, that sounds fun. I haven't contributed here before, but I'll get the inspiration from this PR! Thanks.

@apelisse apelisse mentioned this pull request Aug 14, 2020
/// 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.

/// 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.

@@ -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?

matklad added a commit to matklad/rust that referenced this pull request Sep 4, 2020
Address review comments on `Peekable::next_if`

r? @pickfire
See rust-lang#72310 (review) for context.
matklad added a commit to matklad/rust that referenced this pull request Sep 4, 2020
Address review comments on `Peekable::next_if`

r? @pickfire
See rust-lang#72310 (review) for context.
matklad added a commit to matklad/rust that referenced this pull request Sep 4, 2020
Address review comments on `Peekable::next_if`

r? @pickfire
See rust-lang#72310 (review) for context.
matklad added a commit to matklad/rust that referenced this pull request Sep 4, 2020
Address review comments on `Peekable::next_if`

r? @pickfire
See rust-lang#72310 (review) for context.
matklad added a commit to matklad/rust that referenced this pull request Sep 4, 2020
Address review comments on `Peekable::next_if`

r? @pickfire
See rust-lang#72310 (review) for context.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Sep 6, 2020
Address review comments on `Peekable::next_if`

r? @pickfire
See rust-lang#72310 (review) for context.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Sep 6, 2020
Address review comments on `Peekable::next_if`

r? @pickfire
See rust-lang#72310 (review) for context.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants