Type inference in closures

I am trying to create a trait to accept some sort of callback API but I am running into a problem that Rust can't infer the types when I tried to use a closure implementation of the trait.

Just to illustrate, the following code works fine:

fn run<H, T, E>(handler: H) -> Result<T, E>
where
    H: Fn(String) -> Result<T, E>,
{
    handler("10".to_owned())
}

fn main() {
    run(|n| n.parse::<u8>());
}

But when I try to create a trait instead of using a function on the signature, Rust cannot infer the argument type anymore:

trait Handler<T, E> {
    fn handle(&self, input: String) -> Result<T, E>;
}

impl<F, T, E> Handler<T, E> for F
where
    F: Fn(String) -> Result<T, E>,
{
    fn handle(&self, input: String) -> Result<T, E> {
        self(input)
    }
}

fn run<H, T, E>(handler: H) -> Result<T, E>
where
    H: Handler<T, E>,
{
    handler.handle("10".to_owned())
}

fn main() {
    run(|n| n.parse::<u8>());
}

The compiler returns the following error:

error[E0282]: type annotations needed
  --> src/main.rs:22:10
   |
22 |     run(|n| n.parse::<u8>());
   |          ^ consider giving this closure parameter a type
   |
   = note: type must be known at this point

If I give the n parameter a explicit type (run(|n: String| ...), then everything works, but I would like to know if is there some workaround for this inferece to work.

Edit: simplified a bit the examples

I think this is just a limitation, remember that the Fn traits are just traits, they do have some compiler magic, but importantly it's semantically valid for a single type to implement both FnOnce(String) and FnOnce(usize).

So, from the perspective of the compiler, you might be able to do

run(|n: usize| ...)

and that could still fulfill the requirements of the bounds. I think this is where the required type annotations come from, it doesn't work backwards through more than exactly one trait bound to infer the types of closures.

1 Like

This is actually I think similar to an issue I've been dealing with lately, illustrated by this playground.

I settled on using an identity function with the expected bounds to "fix" the type of the inferred closure.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f0e0a991d1cb3933d659e1a4caa11872

1 Like

Wow, that makes a lot of sense, didn't think about that at all, thanks a lot!

BTW the workaround is also great (and so simple!), thanks!