如何在Rust中实现执行动态调度的通用功能?

时间:2018-12-14 17:28:51

标签: generics rust dynamic-dispatch

在我正在研究的项目中,存在此对象:

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应该代表DebugSomeLocalTrait之类的任意特征。

我也尝试过使用Box::<T>::new()之类的turbo鱼,但无济于事。编译器抱怨T并没有告诉编译器它只是一个特征。有?Sized,但我找不到?IAmTrait。新的Rust 2018 impl Trait语法以类似的方式失败。

我现在通过使用宏创建函数来解决此问题。所以我有dispatch_debugdispatch_cool_trait。本质上是重新实现泛型接口。由于我们想将其用于不相关的特征,因此我无法使用某种形式的父代特征。 DebugDisplay与我们将创建的任何特征都不相关。

有更好的方法吗?在一个完美的世界中,我们将拥有一个dispatch函数或方法,该函数或方法将使我们能够说:

action(foo.dispatch<SomeTrait>())

这是一个沙盒,其中显示了简化版本以开始对话。 https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7b739ab11da15ec793ee46c2d8ac47fc

1 个答案:

答案 0 :(得分:0)

如果您愿意放弃枚举,则可以使用其他特征来进行调度:

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

Full playground example