首页 > 解决方案 > 如何在 Rust 中实现一个执行动态调度的通用函数?

问题描述

在我正在处理的一个项目中,有这个对象:

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这里应该代表任意特征,例如Debugor SomeLocalTrait

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

我现在已经通过使用宏创建函数来解决这个问题。所以我有dispatch_debugor dispatch_cool_trait。本质上是重新实现泛型接口。由于我们想将其用于不相关的特征,我不能使用某种形式的父特征。Debug或者Display与我们将创建的任何特征无关。

有没有更好的办法?在一个完美的世界里,我们会有一个dispatch函数或方法可以让我们说:

action(foo.dispatch<SomeTrait>())

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

标签: genericsrustdynamic-dispatch

解决方案


如果您愿意摆脱枚举,您可以使用其他特征进行调度:

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

完整的游乐场示例


推荐阅读