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 error: "expected associated type" and "found associated type" are the same in some cases with Fn traits #91829

Open
jendrikw opened this issue Dec 12, 2021 · 4 comments
Labels
A-closures Area: closures (`|args| { .. }`) A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: lifetime related D-confusing Diagnostics: Confusing error or lint that should be reworked. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@jendrikw
Copy link
Contributor

Given the following code: play

use std::ops::Not;

fn compose_mut<A, B, C, G, F>(mut f: F, mut g: G) -> impl FnMut(A) -> C
where
    F: FnMut(A) -> B,
    G: FnMut(B) -> C,
{
    move |x| g(f(x))
}

fn predicate(n: &usize) -> bool {
    *n == 0
}

fn main() {
    let b: &dyn FnMut(&usize) -> bool =
        &compose_mut(predicate, bool::not);
    println!("ok")
}

The current output is:

error[E0308]: mismatched types
  --> src/main.rs:17:9
   |
3  | fn compose_mut<A, B, C, G, F>(mut f: F, mut g: G) -> impl FnMut(A) -> C
   |                                                      ------------------
   |                                                      |
   |                                                      the expected opaque type
   |                                                      the found opaque type
...
17 |         &compose_mut(predicate, bool::not);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected associated type `<impl FnMut<(&usize,)> as FnOnce<(&usize,)>>::Output`
              found associated type `<impl FnMut<(&usize,)> as FnOnce<(&usize,)>>::Output`

I don't understand the issue. This part is especially confusing, because both types are the same:

   = note: expected associated type `<impl FnMut<(&usize,)> as FnOnce<(&usize,)>>::Output`
              found associated type `<impl FnMut<(&usize,)> as FnOnce<(&usize,)>>::Output`

I think this has to do with the Fn traits, because it doesn't happen with any other trait, so this code compiles:

trait T {}

struct S;

impl T for S {}

fn return_impl() -> impl T {
    S
}

fn main() {
    let b: &dyn T =
        &return_impl();
    println!("ok")
}

Issue exists in both rustc 1.57.0 (f1edd04 2021-11-29) and rustc 1.59.0-nightly (0b42dea 2021-12-09).

@jendrikw jendrikw 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 Dec 12, 2021
@jendrikw
Copy link
Contributor Author

I think it also has to do with the reference in &usize, could the two types maybe have different lifetime requirements that are not shown?

@BGR360
Copy link
Contributor

BGR360 commented Dec 21, 2021

Interestingly, it's nothing to do with your function declaration; it's the type annotation on b. If you leave off the annotation, it compiles (I've rewritten the function signature to use impl Trait syntax cuz that's a little nicer on my eyes) (play):

use std::ops::Not;

fn compose_mut<A, B, C>(
    mut f: impl FnMut(A) -> B,
    mut g: impl FnMut(B) -> C,
) -> impl FnMut(A) -> C {
    move |x| g(f(x))
}

fn predicate(n: &usize) -> bool {
    *n == 0
}

fn main() {
    let b = compose_mut(predicate, bool::not);
}

To prove that it has the type you want, you can try passing it into a function (play):

fn takes_composed<'a>(_: &dyn FnMut(&'a usize) -> bool) {}

fn main() {
    let b = compose_mut(predicate, bool::not);
    takes_composed(&b);
}

If you remove the lifetime annotation from takes_composed, it reveals the same problem:

fn takes_composed(_: &dyn FnMut(&usize) -> bool) {}
error[E0308]: mismatched types
  --> src/main.rs:18:20
   |
6  | ) -> impl FnMut(A) -> C {
   |      ------------------
   |      |
   |      the expected opaque type
   |      the found opaque type
...
18 |     takes_composed(&b);
   |                    ^^ one type is more general than the other
   |
   = note: expected associated type `<impl FnMut<(&usize,)> as FnOnce<(&usize,)>>::Output`
              found associated type `<impl FnMut<(&usize,)> as FnOnce<(&usize,)>>::Output`

So that's the fundamental problem, I believe: the &usize needs to be "more generic" than the &dyn.

I don't really know of a way to express this kind of type signature as a type annotation on a variable. It may be impossible, or require some sort of "higher-ranked types" thing that I'm too C programmer to understand. I tried adding one of those for<'a> thingamajigs but it made no difference:

let b: &dyn for<'a> FnMut(&'a usize) -> bool =
        &compose_mut(predicate, bool::not);

@rustbot label +A-lifetimes +A-closures +D-confusing +D-terse

@rustbot rustbot added A-closures Area: closures (`|args| { .. }`) A-lifetimes Area: lifetime related D-confusing Diagnostics: Confusing error or lint that should be reworked. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. labels Dec 21, 2021
@LucaCappelletti94
Copy link

Hi all! Any development on this issue?

@estebank
Copy link
Contributor

Current output, little change:

error: implementation of `FnMut` is not general enough
  --> src/main.rs:17:9
   |
17 |         &compose_mut(predicate, bool::not);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnMut` is not general enough
   |
   = note: `impl FnMut(&'2 usize) -> bool` must implement `FnMut<(&'1 usize,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnMut<(&'2 usize,)>`, for some specific lifetime `'2`

error[E0308]: mismatched types
  --> src/main.rs:17:9
   |
17 |         &compose_mut(predicate, bool::not);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected associated type `<impl FnMut(&usize) -> bool as FnOnce<(&usize,)>>::Output`
              found associated type `<impl FnMut(&usize) -> bool as FnOnce<(&usize,)>>::Output`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: closures (`|args| { .. }`) A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: lifetime related D-confusing Diagnostics: Confusing error or lint that should be reworked. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. 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

5 participants