Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting an enum where all variants implement the same trait to a box in Rust?

Tags:

enums

rust

traits

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!

like image 908
Anders Avatar asked Jan 05 '19 21:01

Anders


People also ask

How do you convert enum to string in Rust?

The easiest way to convert an enum to a String in Rust is to implement the std::fmt::Display trait. Then you can call the to_string() method.

Can enums have methods Rust?

In Rust, methods cannot only be defined on structs, they can also be defined on tuples and enums, and even on built-in types like integers.

What is the point of enums in Rust?

The enum keyword allows the creation of a type which may be one of a few different variants. Any variant which is valid as a struct is also valid as an enum .

How do you initialize an enum in Rust?

To initialize an enum with values, we assign the enum with a value to a variable. We write the enum name, followed by double colon operators and the enum value name. Lastly, we specify a value between parentheses. In the example above, we assign 1 to Enabled and 0 to Disabled in the variables yes and no.


1 Answers

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.

like image 167
Anders Kaseorg Avatar answered Sep 22 '22 11:09

Anders Kaseorg