Previously a question was asked about creating an array of functions where the functions returned integers from a range. The final solution was to do a map/collect into a Vec<_>
.
I have a similar yet different situation where I have closures with the same signature but different implementations. I tried this:
let xs: Vec<_> = vec![
move |(x, y)| (y, x),
move |(x, y)| (1 - y, 1 - x),
];
The error I get back:
error[E0308]: mismatched types
--> src/main.rs:4:9
|
4 | move |(x, y)| (1 - y, 1 - x),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
|
= note: expected type `[closure@src/main.rs:3:9: 3:29]`
found type `[closure@src/main.rs:4:9: 4:37]`
= 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 tried boxing:
let xs: Vec<_> = vec![
Box::new(move |x: u8, y: u8| (y, x)),
Box::new(move |x: u8, y: u8| (1 - y, 1 - x)),
];
I get back the same error:
error[E0308]: mismatched types
--> src/main.rs:4:18
|
4 | Box::new(move |x: u8, y: u8| (1 - y, 1 - x)),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
|
= note: expected type `[closure@src/main.rs:3:18: 3:44]`
found type `[closure@src/main.rs:4:18: 4:52]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
What is the right way to box closures so that they can be put into a vector (or an array)?
The problem is that type inference has kicked in before you wanted it to. Conceptually, it's like this:
When the first value is seen, the type of the
Vec
's elements is inferred to be of that type. The second element then causes a mismatch.Even when you
Box
the values, you have the same problem:Looking at it another way, you never actually created a trait object. You have a
Box<ConcreteType>
, not aBox<dyn Trait>
.The solution is to cast the boxed concrete types to the boxed trait object:
The second
push
can have the type coerced automatically, so you can choose to leave theas
bit off of that line.Rolling this back up to the original problem:
Or you can avoid the inference at all by specifying the type on the variable, my preferred style for this:
You should read the suggestion in the error message as "consider boxing your closure and using it as a trait object, or using it just as a trait object".
Using trait object references without boxing them will not work here because nothing owns the closures. The references in the vector would outlive the closures:
The vector needs to take ownership of the closures, which is where boxing the trait objects comes into play:
This explicitly tells the compiler that the vector can contain boxes of any closure with the same interface.