Преобразование перечисления, где все варианты реализуют одну и ту же черту в поле в Rust? - PullRequest
0 голосов
/ 06 января 2019

У меня есть черта Foo, с некоторыми реализациями, вместе с перечислением Foos с одним вариантом на реализацию. Я хочу иметь возможность конвертировать мое перечисление в Box<dyn Foo>.

Это мое текущее решение:

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),
        }
    }
}

Работает, но в into_enum много котельной плиты. По мере того, как количество вариантов будет расти, будет расти и функция. Есть ли более простой способ сделать это? Такое ощущение, что это должен быть один лайнер!

Ответы [ 2 ]

0 голосов
/ 06 января 2019

С enum_dispatch ящиком вы можете написать

#[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),
}

, чтобы получить сгенерированный impl Foo for Foos. Затем вы можете конвертировать Foos в Box<dyn Foo> просто Box::new.

У этого подхода есть потенциальная обратная сторона: Box::new(Foos::A(FooA)) содержит Foos, а не FooA, поэтому он потребует дополнительных затрат при динамической диспетчеризации от dyn Foo до Foos и enum отправка от Foos до FooA.

С другой стороны, теперь, когда у вас есть impl Foo for Foos: везде, где вы использовали бы Box<dyn Foo>, вы вместо этого сможете напрямую использовать Foos, что должно быть более эффективным во всех отношениях.

0 голосов
/ 06 января 2019

Я недавно хотел что-то подобное. Я не могу предложить вам однострочник, но макрос, который автоматически генерирует соответствующие руки match вместе с вариантами enum:

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),
);

Таким образом, есть только одно место для поддержания всех возможностей, все остальное генерируется. Может быть, даже ящик enum_dispatch поможет.

Не по теме: действительно ли это должно быть into_enum(self)->Box<dyn Foo>? Разве это не должно быть что-то вроде as_foo(&self)->&dyn Foo?

...