如何避免动态调度?

Kur*_*aum 2 rust

我具有以下特征:

struct ArtistInfo {
    // some fields
}

pub trait Fetcher {
    fn fetch(&self, artist: String) -> ArtistInfo;
}
Run Code Online (Sandbox Code Playgroud)

我想拥有几个可以在不同情况下使用的提取程序。我的第一个直觉是伸手去拿地图并使用trait对象,如下所示:

type MusicService = String;
let fetchers: HashMap<MusicService, Box<Fetcher>> = HashMap::new();
Run Code Online (Sandbox Code Playgroud)

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

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

Fra*_*gné 5

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

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

然后,Box<Fetcher>可以使用代替使用AnyFetcher。您必须match依靠枚举自己进行分派,但是您将分派给静态已知的方法,因此这样做的好处是CPU能够看到函数调用的目的地(相反,真正的动态呼叫)。

// 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您采用这种方法,那么您可能会意识到,Fetcher此刻特质实际上并没有达到目的。fetch可能也是每种提取程序类型的固有方法。

  • 我想框状特征的优点在于,使用此库的人可以为自己的结构扩展`Fetcher`特征,而添加另一个枚举变体(实现该结构的`fetcher()`方法将更困难)。直接)到“ AnyFetcher”(不是(重新)定义自己的“ AnyFetcher”枚举)? (3认同)
  • @Evert如果混合了已知和未知的实现,则可以向枚举添加一个包含`Box &lt;Fetcher&gt;`的变体,以处理未知的实现。如果您*仅*具有未知的实现(或已知的实现不足以保证自己的enum变体不重要),那么您将避免动态分配(除非您采用极端的方法,例如动态代码生成)。 (2认同)
  • 更直白地说明这一点:如果您有代码的 *dynamic* 实现,则无法避免 *dynamic* 分派。这完全取决于您对代码的要求。 (2认同)