Как реализовать универсальную функцию в Rust, которая выполняет динамическую диспетчеризацию? - PullRequest
0 голосов
/ 14 декабря 2018

В проекте, над которым я работаю, есть этот объект:

enum ContainedType {
    SomeType,
    OtherType,
    ...
}
struct OurObject {
    contains: ContainedType,
    ...
}

impl OurObject {
    pub fn unpack_sometype(self) -> AType { ... }
    pub fn unpack_othertype(self) -> BType { ... }
    ...
}

OurObject - это контейнер вещей, которые каким-то образом «упакованы».Каждая вещь, которая может быть упакована, реализует различные черты.

Мы заканчиваем повторяющимся кодом вроде:

match foo.type() {
    SomeType => action(foo.unpack_sometype()),
    OtherType => action(foo.unpack_othertype()),
    ...
}

Я хотел бы выделить код match в функцию, чтобы мы могли отправитьна произвольную черту.

action(foo)

Однако я сталкиваюсь с проблемами ...

pub fn dispatch<T>(obj: OurObject) -> Box<T> {
    match obj.type() {
        SomeType => Box::new(obj.unpack_sometype()),
        OtherType => Box::new(obj.unpack_othertype()),
        ...
    }
}

T здесь предполагается заменить произвольную черту, такую ​​как Debugили SomeLocalTrait.

Я также пытался использовать турбовину, как Box::<T>::new(), но безрезультатно.Компилятор жалуется, что T не говорит компилятору, что это только черта.Есть ?Sized, но я не могу найти ?IAmTrait.Новый синтаксис Rust 2018 impl Trait не работает аналогичным образом.

Пока что я обошел это, создав функции с помощью макроса.Итак, у меня есть dispatch_debug или dispatch_cool_trait.По существу, повторная реализация универсального интерфейса.Поскольку мы хотим использовать это для несвязанных черт, я не могу использовать некоторую форму родительской черты.Debug или Display не связаны с какой-либо чертой, которую мы создали бы.

Есть ли лучший способ?В идеальном мире у нас была бы dispatch функция или метод, который позволил бы нам сказать:

action(foo.dispatch<SomeTrait>())

Это песочница, показывающая упрощенную версию для начала разговора.https://play.rust -lang.org /? Версия = стабильные и режим = отлаживать и издание = 2018 & сути = 7b739ab11da15ec793ee46c2d8ac47fc

1 Ответ

0 голосов
/ 15 декабря 2018

Если вы хотите отойти от перечислений, вы можете использовать дополнительные черты для отправки:

trait ObjectTrait {
    fn example1(&self);
    fn example2(&self);
}

struct Object2<E> {
    thing: E,
}

impl<E: fmt::Debug + MeaninglessNumeric> ObjectTrait for Object2<E> {
    fn example1(&self) {
        println!("{:?}", self.thing);
    }
    fn example2(&self) {
        println!("{}", self.thing.numeric());
    }
}

fn example3(o: &ObjectTrait) {
    o.example1();
}
fn example4(o: &ObjectTrait) {
    o.example2();
}

После этого вы можете настроить свою основную:

fn main() {
    let obj1 = OurObject {
        contains: ContainedType::SomeType,
    };
    let obj2 = OurObject {
        contains: ContainedType::OtherType,
    };
    let obj3 = Object2 {
        thing: AType { value: 5 },
    };
    let obj4 = Object2 {
        thing: BType {
            contents: "Hello, World".to_string(),
        },
    };

    example1(obj1);
    example2(obj2);
    example3(&obj3);
    example4(&obj4);
}

Пример полной игровой площадки

...