如何避免动态调度? [英] How can I avoid dynamic dispatch?

查看:29
本文介绍了如何避免动态调度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下特点:

struct ArtistInfo {
    // some fields
}

pub trait Fetcher {
    fn fetch(&self, artist: String) -> ArtistInfo;
}

我想要几个不同的提取器,可以在不同的情况下使用.我的第一直觉是找到一张地图并像这样使用 trait 对象:

I want to have several different fetchers that I can use under different circumstances. My first instinct is to reach for a map and use trait objects like so:

type MusicService = String;
let fetchers: HashMap<MusicService, Box<Fetcher>> = HashMap::new();

这将允许我在运行时配置可用的音乐服务集.

This will allow me to make the set of available music services configurable at run time.

这将导致对我的每个 Fetcher 进行动态调度.我猜测这种鸭子类型是一种非常面向对象的方法来解决手头的问题.是否有可能避免动态调度的不同方法?

This will result in dynamic dispatch for each of my Fetchers. I'd hazard to guess that this kind of duck typing is a very Object-Oriented way of approaching the problem at hand. Is there potentially a different approach that would avoid the dynamic dispatching?

推荐答案

如果你事先知道你将使用的所有 Fetcher 类型,你可以定义一个 enum 包含每种类型的变体.

If you know in advance all the types of Fetchers that you'll be using, you can define an enum containing a variant for each type.

pub enum AnyFetcher {
    Fetcher1(Fetcher1),
    Fetcher2(Fetcher2),
    Fetcher3(Fetcher3),
//  ^^^^^^^^ ^^^^^^^^
//      |        |
//      |      name of a struct/enum that implements `Fetcher`
//      |
//    name of the enum variant
}

然后,您可以使用 AnyFetcher 代替 Box.您必须在枚举上 match 自己进行分派,但您将分派到静态已知方法,因此这样做的好处是 CPU 将能够看到目标函数调用(与真正的动态调用相反).

Then, instead of using Box<Fetcher>, you can use AnyFetcher. You'll have to match on the enum to do the dispatching yourself, but you'll be dispatching to statically known methods, so this has the benefit that the CPU will be able to see the destination of the function call (contrarily to a true dynamic call).

// AnyFetcher doesn't necessarily have to implement Fetcher.
impl Fetcher for AnyFetcher {
    fn fetch(&self, artist: String) -> ArtistInfo {
        match *self {
            AnyFetcher::Fetcher1(ref fetcher) => fetcher.fetch(artist),
            AnyFetcher::Fetcher2(ref fetcher) => fetcher.fetch(artist),
            AnyFetcher::Fetcher3(ref fetcher) => fetcher.fetch(artist),
//                                   ^^^^^^^     ^^^^^^^^^^^^^^^^^^^^^
//                                      |                  |
//                                      |        these are static calls...
//                                      |
//              ...because each fetcher variable has a distinct type,
//              which is the type of a concrete Fetcher implementation
        }
    }
}

如果您采用这种方法,您可能会意识到 Fetcher 特性此时实际上并没有起到任何作用;fetch 也可以是每个 fetcher 类型的固有方法.

If you're going with this approach, you may realize that the Fetcher trait doesn't actually serve a purpose at this point; fetch could just as well be an inherent method on each fetcher type.

这篇关于如何避免动态调度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆