我具有以下特征:
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 动态分配。我很容易猜测这种鸭子输入是解决当前问题的一种非常面向对象的方式。是否有可能避免动态分配的其他方法?
如果您事先知道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可能也是每种提取程序类型的固有方法。