Reputation: 8577
I have a trait Foo
, with some implementations, together with an enum Foos
with one variant per implemention. I want to be able to convert my enum into Box<dyn Foo>
.
This is my current solution:
trait Foo {}
struct FooA {}
impl Foo for FooA {}
struct FooB {}
impl Foo for FooB {}
struct FooC {}
impl Foo for FooC {}
enum Foos {
A(FooA),
B(FooB),
C(FooC),
}
impl Foos {
fn into_box(self) -> Box<dyn Foo> {
match self {
Foos::A(foo) => Box::new(foo),
Foos::B(foo) => Box::new(foo),
Foos::C(foo) => Box::new(foo),
}
}
}
It works, but there's a lot of boiler plate in into_enum
. As the number of variants grow, so will the function. Is there a simpler way to do this? It feels like it should be a one liner!
Upvotes: 9
Views: 4662
Reputation: 29091
It is very easy to convert from enum to trait, and then Box that. Yes a bit boilerplate, but not as much as posted, reusable, and your IDE should create all the arms.
trait MyTrait {
}
enum MyEnum {
Var1(SomeTraitStruct),
Var2(AnotherTraitStruct),
}
impl MyEnum {
fn to_my_trait(&self) -> &dyn MyTrait {
match self {
MyEnum::Var1(it) => { it }
MyEnum::Var2(it) => { it }
}
}
}
fn test() {
let value = MyEnum::Var1(SomeTraitStruct{})
let my_trait = value.to_my_trait();
let my_box:Box<&dyn MyTrait> = Box::new(my_trait);
}
Upvotes: 0
Reputation: 3875
With the enum_dispatch
crate, you can write
#[macro_use]
extern crate enum_dispatch;
#[enum_dispatch]
trait Foo {}
struct FooA {}
impl Foo for FooA {}
struct FooB {}
impl Foo for FooB {}
struct FooC {}
impl Foo for FooC {}
#[enum_dispatch(Foo)]
enum Foos {
A(FooA),
B(FooB),
C(FooC),
}
to get a generated impl Foo for Foos
. You can then convert Foos
to Box<dyn Foo>
with simply Box::new
.
There is a potential downside to this approach: Box::new(Foos::A(FooA))
contains a Foos
, not an FooA
, so it will incur the overhead of both the dynamic dispatch from dyn Foo
to Foos
and the enum
dispatch from Foos
to FooA
.
On the other hand, now that you have impl Foo for Foos
: everywhere you would have used Box<dyn Foo>
, you’ll instead be able to directly use Foos
, which should be more efficient in every way.
Upvotes: 6
Reputation: 35983
I recently wanted something similar. I cannot offer you a one-liner, but a macro that automatically generates the respective match
arms along with then enum
variants:
macro_rules! impl_foos{($($enumvariant: ident($foo: ty),)*) => {
enum Foos {
$($enumvariant($foo),)*
}
impl Foos {
fn into_enum(self) -> Box<dyn Foo> {
match self {
$(Foos::$enumvariant(foo) => Box::new(foo),)*
}
}
}
}}
impl_foos!(
A(FooA),
B(FooB),
C(FooC),
);
This way, there is only one place to maintain all the possibilities, everything else is generated. Maybe even the crate enum_dispatch helps.
Off-topic: Should it really be into_enum(self)->Box<dyn Foo>
? Shouldn't it be something like as_foo(&self)->&dyn Foo
?
Upvotes: 5