How to create a vector of boxed closures in Rust?
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:
// This fails
let xs: Vec<&Fn((i32, i32)) -> (i32, i32)> = vec![
&move |(x, y)| (y, x),
&move |(x, y)| (1 - y, 1 - x),
];
The vector needs to take ownership of the closures, which is where boxing the trait objects comes into play:
let xs: Vec<Box<Fn((i32, i32)) -> (i32, i32)>> = vec![
Box::new(move |(x, y)| (y, x)),
Box::new(move |(x, y)| (1 - y, 1 - x)),
];
This explicitly tells the compiler that the vector can contain boxes of any closure with the same interface.
The problem is that type inference has kicked in before you wanted it to. Conceptually, it's like this:
let mut xs: Vec<_> = Vec::new();
xs.push(Type1);
xs.push(Type2);
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:
let mut xs: Vec<_> = Vec::new();
xs.push(Box::new(Type1));
xs.push(Box::new(Type2));
Looking at it another way, you never actually created a trait object. You have a Box<ConcreteType>
, not a Box<dyn Trait>
.
The solution is to cast the boxed concrete types to the boxed trait object:
let mut xs: Vec<_> = Vec::new();
xs.push(Box::new(Type1) as Box<dyn Trait>);
xs.push(Box::new(Type2) as Box<dyn Trait>);
The second push
can have the type coerced automatically, so you can choose to leave the as
bit off of that line.
Rolling this back up to the original problem:
let xs: Vec<_> = vec![
Box::new(move |(x, y)| (y, x)) as Box<dyn Fn((i32, i32)) -> (i32, i32)>,
Box::new(move |(x, y)| (1 - y, 1 - x)),
];
Or you can avoid the inference at all by specifying the type on the variable, my preferred style for this:
let xs: Vec<Box<dyn Fn((i32, i32)) -> (i32, i32)>> = vec![
Box::new(move |(x, y)| (y, x)),
Box::new(move |(x, y)| (1 - y, 1 - x)),
];