2

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.

Herohtar
  • 5,347
  • 4
  • 31
  • 41
ajax2112
  • 228
  • 1
  • 6
  • Does this answer your question? [How to create a vector of boxed closures in Rust?](https://stackoverflow.com/questions/49012277/how-to-create-a-vector-of-boxed-closures-in-rust) – Chayim Friedman Feb 26 '22 at 18:37

2 Answers2

1

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.

Kevin Anderson
  • 6,850
  • 4
  • 32
  • 54
  • Thank you so much. Yeah I think the reason I didn't find that as I kept coming across different ways of doing what seems like the same thing from my perspective as a beginner. i.e. setting the return type to `impl Fn(f32) -> f32`, vs using a Box and the `dyn` keyword versus a type parameter which implements the required closure trait. – ajax2112 Feb 26 '22 at 08:06
1

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.).

Optimistic Peach
  • 3,862
  • 2
  • 18
  • 29
  • Why compiler can't deduce `f32 + 'static` as `f32`? – tejasvi88 Feb 26 '22 at 06:40
  • I'm not sure what you mean, @tejasvi88, would you mind including a playground link? https://play.rust-lang.org/ – Optimistic Peach Feb 26 '22 at 06:44
  • I am sorry please ignore my previous comment, I'm new to rust. I was wondering is there technical limitation for compiler which prevents automatic type inference in this case? Or it is something which will improve with time? – tejasvi88 Feb 26 '22 at 06:56
  • 1
    There isn't a limitation here, nor is there anything to improve in this case other than the error message. It's just how the type deduction algorithm works, and having the type inference automatically cast trait objects would introduce subtle bugs. – Optimistic Peach Feb 26 '22 at 08:12