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

Confusing diagnostic - error[E0308]: mismatched types #84400

Open
elichai opened this issue Apr 21, 2021 · 5 comments
Open

Confusing diagnostic - error[E0308]: mismatched types #84400

elichai opened this issue Apr 21, 2021 · 5 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions D-confusing Diagnostics: Confusing error or lint that should be reworked. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@elichai
Copy link
Contributor

elichai commented Apr 21, 2021

https://play.rust-lang.org/?gist=d912ef0e107be493dd10b72f973897ba

use serde::*;

#[derive(Serialize)]
struct Test;

fn main() {
    serialize_wrap(serialize, &Test);
}

fn serialize_wrap<F: FnOnce(&Test)>(f: F, ser: &Test) {
    f(ser)
}
fn serialize<T: Serialize>(x: T) {
    todo!()
}

The current output is:

error[E0308]: mismatched types
  --> src/main.rs:7:5
   |
7  |     serialize_wrap(serialize, &Test);
   |     ^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected type `FnOnce<(&Test,)>`
              found type `FnOnce<(&Test,)>`
note: the lifetime requirement is introduced here
  --> src/main.rs:10:22
   |
10 | fn serialize_wrap<F: FnOnce(&Test)>(f: F, ser: &Test) {
   |                      ^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

I'm not quite sure what's the right diagnostic here, maybe that it should be by value?

@elichai elichai 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 Apr 21, 2021
@elichai elichai changed the title Weird diagnostic - error[E0308]: mismatched types Confusing diagnostic - error[E0308]: mismatched types Apr 21, 2021
@estebank estebank added A-lifetimes Area: Lifetimes / regions D-confusing Diagnostics: Confusing error or lint that should be reworked. labels Apr 21, 2021
@estebank
Copy link
Contributor

The output in beta is

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:7:5
  |
7 |     serialize_wrap(serialize, &Test);
  |     ^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: `fn(&'2 Test) {serialize::<&'2 Test>}` must implement `FnOnce<(&'1 Test,)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&'2 Test,)>`, for some specific lifetime `'2`

You can make this compile by adding a named lifetime tying f and ser, but that might not be what you want to do, depending on the specifics.

@elichai
Copy link
Contributor Author

elichai commented Apr 22, 2021

@estebank, Thanks for responding.
your fix allows me to understand the problem, if I got it correctly it means that:
The compiler assumes that the &Test input to serialize_wrap and the &Test input to FnOnce have different lifetimes.
by introducing a named lifetime we tell it that they need to have the same lifetime.

The new diagnostic is obviously better but I also don't think I fully understand it to be honest.

@estebank
Copy link
Contributor

The diagnostic absolutely needs to be improved. The reason this doesn't compile is because the signature desugars to fn serialize_wrap<'b, F: for<'a> FnOnce(&'a Test)>(f: F, ser: &'b Test).

And that is a problem because the compiler can't ensure that 'b will live when F gets called.

@jonhoo
Copy link
Contributor

jonhoo commented Apr 22, 2021

Here's an even simpler one:

fn check<S: AsRef<str>>(_: S) -> bool {
    false
}

fn main() {
    let options: Vec<String> = Vec::new();
    let _ = options.into_iter().filter(check::<&String>);
}

On stable this produces the unhelpful

error[E0308]: mismatched types
 --> src/main.rs:7:33
  |
7 |     let _ = options.into_iter().filter(check::<&String>);
  |                                 ^^^^^^ one type is more general than the other
  |
  = note: expected type `FnOnce<(&String,)>`
             found type `FnOnce<(&String,)>`

On beta/nightly it's a little better, but still pretty inscrutable:

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:7:33
  |
7 |     let _ = options.into_iter().filter(check::<&String>);
  |                                 ^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: `fn(&'2 String) -> bool {check::<&'2 String>}` must implement `FnOnce<(&'1 String,)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&'2 String,)>`, for some specific lifetime `'2`

In this particular case it can be fixed with:

fn check<S: AsRef<str>>(_: &S) -> bool {
    false
}

fn main() {
    let options: Vec<String> = Vec::new();
    let _ = options.into_iter().filter(check::<String>);
}

But that's not really a general solution. Just sharing in case others run into something similar and ends up here.

@estebank
Copy link
Contributor

estebank commented Jun 27, 2023

Current output:

error[[E0308]](https://doc.rust-lang.org/nightly/error_codes/E0308.html): mismatched types
  --> src/main.rs:8:5
   |
8  |     serialize_wrap(serialize, &Test);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait `for<'a> FnOnce<(&'a Test,)>`
              found trait `FnOnce<(&Test,)>`
note: the lifetime requirement is introduced here
  --> src/main.rs:11:22
   |
11 | fn serialize_wrap<F: FnOnce(&Test)>(f: F, ser: &Test) {
   |                      ^^^^^^^^^^^^^

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:8:5
  |
8 |     serialize_wrap(serialize, &Test);
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: `fn(&'2 Test) {serialize::<&'2 Test>}` must implement `FnOnce<(&'1 Test,)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&'2 Test,)>`, for some specific lifetime `'2`
error[[E0308]](https://doc.rust-lang.org/nightly/error_codes/E0308.html): mismatched types
 --> src/main.rs:7:13
  |
7 |     let _ = options.into_iter().filter(check::<&String>);
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
  |
  = note: expected trait `for<'a> FnMut<(&'a String,)>`
             found trait `FnMut<(&String,)>`
note: the lifetime requirement is introduced here
 --> /rustc/065a1f5df9c2f1d93269e4d25a2acabbddb0db8d/library/core/src/iter/traits/iterator.rs:925:12

error: implementation of `FnOnce` is not general enough
 --> src/main.rs:7:13
  |
7 |     let _ = options.into_iter().filter(check::<&String>);
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
  |
  = note: `fn(&'2 String) -> bool {check::<&'2 String>}` must implement `FnOnce<(&'1 String,)>`, for any lifetime `'1`...
  = note: ...but it actually implements `FnOnce<(&'2 String,)>`, for some specific lifetime `'2`

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 A-lifetimes Area: Lifetimes / regions D-confusing Diagnostics: Confusing error or lint that should be reworked. 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

3 participants