问题
I have the following trait:
struct ArtistInfo {
// some fields
}
pub trait Fetcher {
fn fetch(&self, artist: String) -> ArtistInfo;
}
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.
This will result in dynamic dispatch for each of my Fetcher
s. 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?
回答1:
If you know in advance all the types of Fetcher
s 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
}
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
}
}
}
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.
来源:https://stackoverflow.com/questions/49227286/how-can-i-avoid-dynamic-dispatch