Why does this single use closure need type annotation?

Just for the sake of clarity I defined a portion of code as a closure, like that:

let mut wrap = |action| {
  let () = action();
  last_check_time = Instant::now();
};

wrap(|| {
  ...
});

I'm using the closure in just one place. In the ... argument I do normal stuff like call some functions on the captured variables.

The code portion doesn't compile with:

error[E0282]: type annotations needed
  --> my_code/run.rs:34:30
   |
34 |   let mut wrap = |action| {
35 |       let () = action();
   |                -------- type must be known at this point

but I believe there's no ambiguity left?
This is a problem to me because the type of the closure I pass as an argument is an opaque closure#295. I can't specify the type of wrap to be impl FnMut(impl Fn()) because that's disallowed in closure type annotations.

What can I do and why the type error?

Thanks!

You could pass the action closure as a boxed trait object:

use std::time::Instant;

fn main() {
    let mut last_check_time = Instant::now();
    
    let mut wrap = |action: Box<dyn Fn()>| {
        let () = action();
        last_check_time = Instant::now();
    };

    wrap(Box::new(|| {
        println!("hello");
    }));
}

Playground.

Come on, this is different code now doing different things with the additional heap allocation.

1 Like

In general, closures can have lots of different signatures. There might be no distinctions worth making here, but the compiler isn't perfectly able to detect that and avoid considering this ambiguous.

What can I do

Introduce a function that pins down the function signature you want the closure to have.

fn closure_cast<F: Fn()>(f: F) -> F {
    f
}

...

wrap(closure_cast(|| {
  ...
}));
2 Likes

another solution is introduce another variable to help inference (we can't use impl trait as variable type, but &dyn trait works);

    let mut wrap = |action| {
        let action: &dyn Fn() = &action;
        let () = action();
        last_check_time = Instant::now();
    };

    wrap(|| {});
1 Like

This might just work:

    let mut wrap = |action| {
        fn call(f: impl Fn()) { f() }
        let () = call(action);
        last_check_time = Instant::now();
    };

Probably FnOnce for action would be sufficient, too - or for allowing multiple calls, you could make fn call accept an &impl Fn().

2 Likes

Being able to call a function is based on trait implementations,[1] and something like this is ambiguous too.

    let mut wrap = |usable| {
        // Could be many implementors (or could even be an inherent method)
        let () = usable.into();
        last_check_time = Instant::now();
    };

For example, every closure has a distinct type....


...which leads to something I don't think has been pointed out yet. Closures can't be generic over types in Rust so far, so unless you use type erasure,[2] you'll only be able to pass one closure to wrap.

Instead of inferring or coercing inside the closure, you can just take a &dyn Fn(). You'll need to take a reference at the call sites.

(Alternatively, use a macro instead of this wrap closure.)


  1. three different ones, even ↩︎

  2. dyn Fn() ↩︎

4 Likes

aha, in my case the closure is indeed generic over a type parameter specified in a toplevel function definition.

Interestingly as others pointed out &dyn Fn() or parameterizing explicitly over the Fn type works, which is cool.

Thanks guys!