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

Error message for closure that requires |_| is vague #104690

Closed
schneems opened this issue Nov 21, 2022 · 4 comments · Fixed by #117106
Closed

Error message for closure that requires |_| is vague #104690

schneems opened this issue Nov 21, 2022 · 4 comments · Fixed by #117106
Labels
A-diagnostics Area: Messages for errors, warnings, and lints D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-verbose Diagnostics: Too much output caused by a single piece of incorrect code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@schneems
Copy link
Contributor

schneems commented Nov 21, 2022

Given the following code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2fecf0a7fbaba8101daba7d1a1ec1837

fn main() {
    let number = 2;
    Some(true).filter({
        if number % 2 == 0 {
            number == 0
        } else {
            number != 0
        }
    });
}

The current output is:

Compiling playground v0.0.1 (/playground)
error[[E0277]](https://doc.rust-lang.org/stable/error-index.html#E0277): expected a `FnOnce<(&bool,)>` closure, found `bool`
 --> src/main.rs:3:23
  |
3 |        Some(true).filter({
  |   ________________------_^
  |  |                |
  |  |                required by a bound introduced by this call
4 |  |         if number % 2 == 0 {
  |  |_________-
5 | ||             number == 0
6 | ||         } else {
7 | ||             number != 0
8 | ||         }
  | ||_________- this tail expression is of type `_`
9 |  |     });
  |  |_____^ expected an `FnOnce<(&bool,)>` closure, found `bool`
  |
  = help: the trait `for<'r> FnOnce<(&'r bool,)>` is not implemented for `bool`
note: required by a bound in `Option::<T>::filter`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
Standard Output

Should reference:

|_|

As this is the correct code that compiles:

fn main() {
    let number = 2;
    Some(true).filter(|_| {
        if number % 2 == 0 {
            number == 0
        } else {
            number != 0
        }
    });
}

Using

$ rustc -V
rustc 1.65.0 (897e37553 2022-11-02)

If it helps, I'm trying to find a way to conditionally read in a file only if it exists. I was experimenting with this format:

let blerg = Some(template.target_path.exists()).filter(|_| {
            contents.trim()
                == std::fs::read_to_string(&template.target_path)
                    .unwrap()
                    .trim()
        });

Also worth mentioning in the explanation |_| does not show up at all:

$ rustc --explain E0277 | grep "_"
fn some_func<T: Foo>(foo: T) {
    some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied
fn some_func<T: Foo>(foo: T) {
    some_func(5i32); // ok!
fn some_func<T>(foo: T) {
    some_func(5i32);
fn some_func<T: fmt::Debug>(foo: T) {
    some_func(5i32);
    // some_func(WithoutDebug);
@schneems schneems added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 21, 2022
@asonix
Copy link

asonix commented Nov 22, 2022

Filter's predicate must be a type that implements FnOnce(&T) -> bool

a block created with {} does not implement FnOnce, ti's just a block of code

Adding |_| turns the block into the body of a closure, which takes 1 argument but doesn't use the argument

https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.filter

@asonix
Copy link

asonix commented Nov 22, 2022

Coming back because I also think there's a cleaner way to do this:

let blerg = if template.target_path.exists() {
    std::fs::read_to_string(&template.target_path).unwrap().trim() == contents.trim()
} else {
    false
};

@schneems
Copy link
Contributor Author

With some help from social friends https://ruby.social/@Schneems/109384543463801280. I've got some more language to explain what's happening versus what I was expecting.

I Intended to use a closure with no arguments, but {} is a block, not a closure || {} is a closure. This error:

  | ||_________- this tail expression is of type `_`
9 |  |     });
  |  |_____^ expected an `FnOnce<(&bool,)>` closure, found `bool`
  |
  = help: the trait `for<'r> FnOnce<(&'r bool,)>` is not implemented for `bool`
note: required by a bound in `Option::<T>::filter`

especially:

 the trait `for<'r> FnOnce<(&'r bool,)>` is not implemented for `bool`
note: required by a bound in `Option::<T>::filter`

Was confusing to me. Because in my head, I looked at the docs and filter needs a bool...but so I didn't understand what the FnOnce jazz was complaining about. If you're really familiar with this syntax it makes sense and is saying the right thing, that it's expecting a function that returns a boolean instead of simply a boolean, but that wasn't apparent while I was in the moment.

I think in this case, I would have like to be drawn to this section:

3 |        Some(true).filter({
  |   ________________------_^
  |  |                |
  |  |                required by a bound introduced by this call

Maybe something like

3 |        Some(true).filter({
  |   ________________------_^
  |  |                |
  |  |                missing syntax for  `for<'r> FnOnce<(&'r bool,)>`, consider adding `||` 

Which gets me a different error message and eventually leads me to |_| :

3 |     Some(true).filter(|| {
  |                ^^^^^^ -- takes 0 arguments
  |                |
  |                expected closure that takes 1 argument
  |
help: consider changing the closure to take and ignore the expected argument
  |
3 |     Some(true).filter(|_| {
  |                       ~~~

I don't know how difficult such detection/suggestion would be.

@estebank
Copy link
Contributor

CC #27300, #47763

@estebank estebank added D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-verbose Diagnostics: Too much output caused by a single piece of incorrect code. labels Nov 22, 2022
@bors bors closed this as completed in d09c988 Oct 27, 2023
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Oct 27, 2023
Rollup merge of rust-lang#117106 - estebank:issue-27300, r=petrochenkov

When expecting closure argument but finding block provide suggestion

Detect if there is a potential typo where the `{` meant to open the closure body was written before the body.

```
error[E0277]: expected a `FnOnce<({integer},)>` closure, found `Option<usize>`
  --> $DIR/ruby_style_closure_successful_parse.rs:3:31
   |
LL |       let p = Some(45).and_then({|x|
   |  ______________________--------_^
   | |                      |
   | |                      required by a bound introduced by this call
LL | |         1 + 1;
LL | |         Some(x * 2)
   | |         ----------- this tail expression is of type `Option<usize>`
LL | |     });
   | |_____^ expected an `FnOnce<({integer},)>` closure, found `Option<usize>`
   |
   = help: the trait `FnOnce<({integer},)>` is not implemented for `Option<usize>`
note: required by a bound in `Option::<T>::and_then`
  --> $SRC_DIR/core/src/option.rs:LL:COL
help: you might have meant to open the closure body instead of placing a closure within a block
   |
LL -     let p = Some(45).and_then({|x|
LL +     let p = Some(45).and_then(|x| {
   |
```

Detect the potential typo where the closure header is missing.

```
error[E0277]: expected a `FnOnce<(&bool,)>` closure, found `bool`
  --> $DIR/block_instead_of_closure_in_arg.rs:3:23
   |
LL |        Some(true).filter({
   |  _________________------_^
   | |                 |
   | |                 required by a bound introduced by this call
LL | |/         if number % 2 == 0 {
LL | ||             number == 0
LL | ||         } else {
LL | ||             number != 0
LL | ||         }
   | ||_________- this tail expression is of type `bool`
LL | |      });
   | |______^ expected an `FnOnce<(&bool,)>` closure, found `bool`
   |
   = help: the trait `for<'a> FnOnce<(&'a bool,)>` is not implemented for `bool`
note: required by a bound in `Option::<T>::filter`
  --> $SRC_DIR/core/src/option.rs:LL:COL
help: you might have meant to create the closure instead of a block
   |
LL |     Some(true).filter(|_| {
   |                       +++
```

Partially address rust-lang#27300. Fix rust-lang#104690.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-verbose Diagnostics: Too much output caused by a single piece of incorrect code. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants