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

Unspecific error on Fn trait bounds #115741

Open
jlogan03 opened this issue Sep 10, 2023 · 6 comments
Open

Unspecific error on Fn trait bounds #115741

jlogan03 opened this issue Sep 10, 2023 · 6 comments
Labels
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.

Comments

@jlogan03
Copy link

Code

pub trait MyFn: Fn() -> () {}

fn do_f(f: &dyn MyFn) {}

fn main() {
    let f = || {};
    do_f(&f);
}

Current output

Jamess-MacBook-Pro:benchground jlogan$ cargo build
   Compiling benchground v0.1.0 (/Users/jlogan/git/benchground/benchground)
error[E0277]: the trait bound `[closure@src/fn_bound_repro.rs:6:13: 6:15]: MyFn` is not satisfied
 --> src/fn_bound_repro.rs:7:10
  |
7 |     do_f(&f);
  |          ^^ the trait `MyFn` is not implemented for closure `[closure@src/fn_bound_repro.rs:6:13: 6:15]`
  |
  = note: required for the cast from `&[closure@src/fn_bound_repro.rs:6:13: 6:15]` to `&dyn MyFn<Output = ()>`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `benchground` (lib) due to previous error
Jamess-MacBook-Pro:benchground jlogan$

Desired output

Some explanation of what part doesn't match or how a person might implement their intent would be helpful

Rationale and extra context

No response

Other cases

The error emitted is nearly the same regardless of the types of arguments in the trait and in the closure, but when the arguments are references, the error also lists separate lifetimes for each input, which makes it look like lifetimes are the issue - but it seems that they're not, since the issue persists even with no relevant lifetimes involved

Anything else?

No response

@jlogan03 jlogan03 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 Sep 10, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 10, 2023
@jlogan03
Copy link
Author

I believe the pattern that should be suggested there is something like this, using function pointers instead of a reference to a closure:

pub type MyFn = fn() -> ();

fn do_f(f: MyFn) {}

fn main() {
    let f = || {};
    do_f(f);
}

@compiler-errors
Copy link
Member

compiler-errors commented Sep 10, 2023

The issue here is that there is no automatic implementation for MyFn. One way to fix this is to provide a blanket implementation:

impl<F> MyFn for F where F: Fn() {}

I guess we could suggest a blanket implementation, but I'm somewhat skeptical of the value of that suggestion, since it heavily depends on what the user is trying to do with the trait (and the existence of other impls that the blanket impl would conflict with).

@compiler-errors
Copy link
Member

I believe the pattern that should be suggested there is something like this, using function pointers instead of a reference to a closure

This would only work if the closure doesn't capture anything from its environment. If it does, then the closure is a distinct type that cannot be coerced to a function pointer, and you'll get errors saying something like:

note: closures can only be coerced to `fn` types if they do not capture any variables
 --> src/main.rs:7:29
  |
7 |     let f = || { println!("{capture}") };
  |                             ^^^^^^^ `capture` captured here

@jlogan03
Copy link
Author

jlogan03 commented Sep 10, 2023

Hmm, good point. I might be wrong here as well, but I don't think there's a way to implement a trait on a specific closure, so the blanket impl would be the only way to make the exact usage from the original example work... but this could definitely subvert the intent of the trait itself.

I know diagnostics don't usually suggest multiple options and this would be pretty verbose, but maybe the suggestion could present both options (blanket impl or change trait to function pointer type alias) with some discussion of the tradeoff?

@compiler-errors
Copy link
Member

compiler-errors commented Sep 10, 2023

I don't think it makes sense in general to turn a &dyn Trait argument (with a Fn supertrait) into a fn pointer argument. It may have fixed your specific example, but it's too specific to be worth suggesting -- it both requires the compiler to do a lot mechanically, and also imo leads the user astray from one of the main usages of closures (capturing).

I've added a note in #115741 that points out if a trait has no implementations, but it doesn't go the full way to suggesting how such a blanket impl would be written -- I fear that would be too "specific" for other cases.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Sep 11, 2023
Point out if a local trait has no implementations

Slightly helps with rust-lang#115741
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Sep 11, 2023
Point out if a local trait has no implementations

Slightly helps with rust-lang#115741
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Sep 11, 2023
Point out if a local trait has no implementations

Slightly helps with rust-lang#115741
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Sep 11, 2023
Rollup merge of rust-lang#115743 - compiler-errors:no-impls, r=davidtwco

Point out if a local trait has no implementations

Slightly helps with rust-lang#115741
@jlogan03
Copy link
Author

Copy, makes sense. Thanks!

@saethlin saethlin removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 11, 2023
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 T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants