我具有以下特征:

struct ArtistInfo {
    // some fields
}

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

我想有几种不同的提取器,可以在不同的情况下使用。我的第一个直觉是伸手去拿一张 map ,并使用trait对象,如下所示:
type MusicService = String;
let fetchers: HashMap<MusicService, Box<Fetcher>> = HashMap::new();

这将使我能够在运行时配置一组可用的音乐服务。

这将导致动态分配我的每个Fetcher。我很容易猜测这种鸭子输入是解决当前问题的一种非常面向对象的方式。是否有可能避免动态分配的其他方法?

最佳答案

如果您事先知道将要使用的Fetcher的所有类型,则可以定义一个enum,其中包含每种类型的变体。

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

然后,可以使用Box<Fetcher>而不是AnyFetcher。您必须在枚举上使用match自己进行分派(dispatch),但是您将分派(dispatch)给静态已知的方法,因此这样做的好处是CPU能够看到函数调用的目的地(反之,则为true动态调用)。
// 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可能也是每种访存程序类型上的固有方法。

关于rust - 如何避免动态调度?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49227286/

10-11 02:54