The and/or
is slightly too unspecific in the error message at hand. The important part of that suggestion is the trait object; the boxing ist just also generally needed for trait objects, though in short-lived cases, working with references instead of Box
es might work and save an allocation. (I assume that’s where the “or
” comes from in that error message in the first place.)
To solve the problem at hand by combining boxing and trait objects, the first thing beyond your approach of trying boxing is to make sure to put the whole closure into a box, not just the return value.
So something like Box::new(|v| Foo::Bar(v))
, instead of the |v| Box::new(Foo::Bar(v))
you had.
Then, to use the trait object type, putting an (ultimately unrelated but identically named) local variable b
with Box<dyn FnOnce(i32) -> Foo>
signature does not help. And without signatures, the compiler still complains.
It’s necessary to convinve the compiler to coerce the boxed closure into a boxed trait object, at least explicitly for the first array element. After that, type inference will help do the rest.
enum Foo {
Bar(usize),
Baz(usize),
}
pub fn main() {
for (a, b) in [
//
(42, Box::new(|v| Foo::Bar(v)) as Box<dyn FnOnce(_) -> _>),
//
(42, Box::new(|v| Foo::Baz(v))),
] {
//
}
}
As you can see, it isn’t even necessary in this case to write down the exact function signature into the FnOnce
trait bound, but you might prefer to write it if you want to be more explicit.
If the goal is to avoid the “asymmetry” of specifying a type only on the first element, to name just one alternative that comes to mind, it’s also quite possible to write down the array type on a local variable first.
pub fn main() {
let array: [(i32, Box<dyn FnOnce(_) -> _>); 2] = [
//
(42, Box::new(|v| Foo::Bar(v))),
//
(42, Box::new(|v| Foo::Baz(v))),
];
for (a, b) in array {
//
}
}
As @H2CO3 mentioned above, in case the functions don’t capture any variables, you could also use function pointers instead; or, it that’s not good for your use-case depending what the use-case looks like, possibly you might want something like FnMut
or Fn
instead, and you might possibly want to add a Send
and/or Sync
bound. E.g. something that looks like Box<dyn FnMut(_) -> _ + Send>
, to show just one example of ways the trait object type could be changed. Don’t worry much about the type at first though, it’s going to be an easy re-factor in case you only figure out the precise requirements at a later time.
Edit: I didn’t even quite expect this to work, but apparently, it’s also an option to use as […array type…]
after the array directly to make this compile. For example
pub fn main() {
for (a, b) in [
//
(42, Box::new(|v| Foo::Bar(v))),
//
(42, Box::new(|v| Foo::Baz(v))),
] as [(_, Box<dyn FnOnce(_) -> _>); 2]
{
//
}
}
or for example
pub fn main() {
for (a, b) in [
//
(42, Foo::Bar),
//
(42, Foo::Baz),
] as [(_, fn(_) -> _; 2]
{
//
}
}
Not that that’s necessarily nicer-looking than defining a local variable for the array, but it does list yet another approach of writing a type signature hint that helps the compiler sufficiently.