Why is type annotation needed when type is explicit as `Self` in generic bound?

Given the following code:

trait A: Sized {
    fn fn_to_fn<Fn0, Fn1>(self, f: Fn0) -> ()
    where
        Fn0: Fn() -> Option<Fn1>,
        Fn1: Fn(&mut Self),
    {
        ()
    }
}

impl<T> A for T {}

fn a() {
    let integer = 0u32;
    integer.fn_to_fn(|| Some(|mut integer| *integer += 1));
}

I get the following error:

error[E0282]: type annotations needed
  --> src/lib.rs:81:31
   |
81 |     integer.fn_to_fn(|| Some(|mut integer| *integer += 1));
   |                               ^^^^^^^^^^^  -------- type must be known at this point
   |
help: consider giving this closure parameter an explicit type
   |
81 |     integer.fn_to_fn(|| Some(|mut integer: /* Type */| *integer += 1));
   |                                          ++++++++++++

I don't understand why type annotation is needed when Fn1 is explicit as implementing Fn(&mut Self). Even my IDE identifies the type of integer within the F1 enclosure as &mut u32, however the compiler needs me to annotate the type. Can someone explain why this is the case, and if possible, suggest how I can avoid type annotation?

One additional observation is that replacing the return type of F0 (Option<F1>) with just F1 no longer needs type annotation...

2 Likes

I think it's about (in theory) a single type can implement difference Fn at the same time. So (in theory) in the general case the type is indeed unknown.

On the topic of why using bare Fn1 works, I just assume the compiler did some special case magic.

Don't count on my words though, just giving out some intuition.

I think the problem is that Some is a function, and that's impeding inference. Even if you have a Fn0: Fn() -> Fn1 bound, if you wrap the Fn1 closure in an identity function (like fn id<T>(t: T) -> T { t }, or wrapping the closure in Some(...).unwrap()), type annotations are required.

By your IDE, I am guessing you mean 'rust-analyzer'? I don't know why I spent so much time on this :'D but here's what I have...

There seems to be a difference between how rust-analyzer resolves this.

vs. how the compiler resolves this... but I can be wrong. It looks like there's some sort of subtle timing issue in how Option<Fn1> vs direct Fn1 is inferred.

That's correct, rust-analyzer correctly infers the type. I guess for the moment this is some kind of limitation within the compiler. I'm very interested in any further input, but I guess for the initial issue itself, for the moment there seems to be no solution other than just making the type explicit. It's very strange though...