Unable to return a Vec<Box<dyn Fn(f32) -> f32>>

Viewed 262

I am trying to return a vector of closures from a function in Rust. Each of which will ultimately be stored on a struct which holds a Box<dyn Fn(f32) -> f32>. Here is a simple reproduction of the error:

fn something() -> Vec<Box<dyn Fn(f32) -> f32>> {
    let mut vec = Vec::new();

    for i in 0..5 {
        vec.push(
            Box::new(
                |t : f32| { t + 1.0 }
            )
        );
    }

    vec
}
error[E0308]: mismatched types
  --> src\main.rs:13:5
   |
2  | fn something() -> Vec<Box<dyn Fn(f32) -> f32>> {
   |                   ---------------------------- expected `Vec<Box<(dyn Fn(f32) -> f32 + 'static)>>` because of return 
type
...
8  |                 |t : f32| { t + 1.0 }
   |                 --------------------- the found closure
...
13 |     vec
   |     ^^^ expected trait object `dyn Fn`, found closure
   |
   = note: expected struct `Vec<Box<(dyn Fn(f32) -> f32 + 'static)>>`
              found struct `Vec<Box<[closure@src\main.rs:8:17: 8:38]>>`

I don't understand this error and where it is coming from, given that the provided closure implements the required trait. I am new to Rust and struggling the most with closures.

2 Answers

Your google-fu needed a bit more jazz: How to create a vector of boxed closures in Rust?

The answer is to replace this line:

let mut vec = Vec::new();

With this:

let mut vec: Vec<Box<dyn Fn(f32) -> f32>> = Vec::new();

Because you've told the compiler explicitly what that Vec should be, it then acts correctly, whereas otherwise it just takes the type from the first element added. The linked issue explains it more. Specifying that variable as your return from the function isn't enough to tie it to the return type of the function.

This happens because the concrete type of the closure you specified takes precedence over the return type when inferring the type of the Vec. In other words, since the closure itself, |t : f32| { t + 1.0 } has an anonymous yet concrete type, it is inferred to be the type of the Vec.

This can be fixed by explicitly ascribing the type:

let mut vec: Vec<Box<dyn Fn(f32) -> f32>> = Vec::new();

Or, by telling the compiler that we still don't know the type, letting it then infer from the return type:

vec.push(
    Box::new(
        |t : f32| { t + 1.0 }
    ) as Box<_>
);

Notice the added as Box<_>.

As a general rule of thumb, Rust is probably able to infer the type correctly, just that its inference rules sometimes tell it to stick to the earliest concrete type. When you hit a snag such as this one, just try tacking on as Box<_> or otherwise (such as as _, or as Arc<_>, etc.).

Related