我有以下特点:
struct ArtistInfo {
// some fields
}
pub trait Fetcher {
fn fetch(&self, artist: String) -> ArtistInfo;
}
我希望在不同情况下可以使用几种不同的取件器。我的第一直觉是到达地图并使用特征对象:
type MusicService = String;
let fetchers: HashMap<MusicService, Box<Fetcher>> = HashMap::new();
这将允许我在运行时配置可用的音乐服务集。
这将导致我的Fetcher
每个动态调度。我猜测这种鸭子打字是一种非常面向对象的方式来解决手头的问题。是否有可能采用不同的方法来避免动态调度?
答案 0 :(得分:5)
如果您事先知道您将使用的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
自己进行调度,但是您将调度静态已知的方法,因此这样做的好处是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
}
}
}
如果你采用这种方法,你可能会发现Fetcher
特征在这一点上实际上没有用处; fetch
也可以是每种抓取器类型的固有方法。