Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the inferred type of a vector of closures?

Tags:

rust

I tried to create vector of closures:

fn main() {
    let mut vec = Vec::new();

    vec.push(Box::new(|| 10));
    vec.push(Box::new(|| 20));

    println!("{}", vec[0]());
    println!("{}", vec[1]());
}

That yielded the following error report:

error[E0308]: mismatched types
 --> src/main.rs:5:23
  |
5 |     vec.push(Box::new(|| 20));
  |                       ^^^^^ expected closure, found a different closure
  |
  = note: expected type `[closure@src/main.rs:4:23: 4:28]`
             found type `[closure@src/main.rs:5:23: 5:28]`
  = note: no two closures, even if identical, have the same type
  = help: consider boxing your closure and/or using it as a trait object

I fixed it by specifying the type explicitly:

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

What is the inferred type of vec and why is it that way?

like image 387
VRW Avatar asked Mar 31 '15 15:03

VRW


1 Answers

Each closure has an auto-generated, unique, anonymous type. As soon as you add the first closure to the vector, that is the type of all items in the vector. However, when you try to add the second closure, it has a different auto-generated, unique, anonymous type, and so you get the error listed.

Closures are essentially structs that are created by the compiler that implement one of the Fn* traits. The struct contains fields for all the variables captured by the closure, so it by definition needs to be unique, as each closure will capture different numbers and types of variables.

Why can't it just infer Box<Fn() -> i32>?

"can't" is a tough question to answer. It's possible that the compiler could iterate through all the traits of every type that is used to see if some intersection caused the code to compile, but that feels a bit magical to me. You could try opening a feature request or discussing it on one of the forums to see if there is general acceptance of such an idea.

However, Rust does try to make things explicit, especially things that might involve performance. When you go from a concrete struct to a trait object, you are introducing indirection, which has the possibility of being slower.

Right now, the Fn* traits work the same as a user-constructed trait:

trait MyTrait {
    fn hello(&self) {}
}

struct MyStruct1;
impl MyTrait for MyStruct1 {}

struct MyStruct2;
impl MyTrait for MyStruct2 {}

fn main() {
    let mut things = vec![];
    things.push(MyStruct1);
    things.push(MyStruct2);
}
error[E0308]: mismatched types
  --> src/main.rs:14:17
   |
14 |     things.push(MyStruct2);
   |                 ^^^^^^^^^ expected struct `MyStruct1`, found struct `MyStruct2`
   |
   = note: expected type `MyStruct1`
              found type `MyStruct2`
like image 196
Shepmaster Avatar answered Oct 13 '22 18:10

Shepmaster