Consider this code that defines a trait RefGen
, a structure Facade
that has a property that can hold a RefGen
and two different new_*
methods that would pick different concrete implementations for the RefGen
.
trait RefGen {
fn gen() -> String;
}
struct FooGen;
impl RefGen for FooGen {
fn gen() -> String {
"foo".to_owned()
}
}
struct BarGen;
impl RefGen for BarGen {
fn gen() -> String {
"bar".to_owned()
}
}
struct Facade<R: RefGen> {
gen: R
}
impl Facade<FooGen> {
fn new_foogen() -> Self {
Facade {
gen: FooGen
}
}
}
impl Facade<BarGen> {
fn new_bargen() -> Self {
Facade {
gen: BarGen
}
}
}
fn main () {}
It works, but it baffles me I would need two impl
blocks for that?
If you want named methods for each type, this is the only way. If you want to do this generically, you can use a generic impl:
impl<R: RefGen> Facade<R> {
fn new() -> Self {
Facade {
gen: R::new(),
}
}
}
This of course requires the RefGen
trait to offer some kind of new
function to create instances:
trait RefGen {
fn gen() -> String;
fn new() -> Self where Self: Sized;
}
The where Self: Sized
bound is there so the trait is still object safe. This means that you can create a Box<RefGen>
, but not call the new
method.
Though now you cannot call the gen
method on your GenFoo
or GenBar
object, because it's a static method. You need to add a &self
parameter to it. In case you never want to actually create an object of type GenFoo
or GenBar
, because they will never have fields, you can get rid of the gen
field and new
constructor entirely by doing all dispatch at compile-time:
struct Facade<R: RefGen> {
gen: PhantomData<R>,
}
impl<R: RefGen> Facade<R> {
fn new() -> Self {
Facade {
gen: PhantomData,
}
}
fn gen(&self) -> String {
R::gen()
}
}
impl Facade<FooGen> {
fn new_foogen() -> Self {
Self::new()
}
}
impl Facade<BarGen> {
fn new_bargen() -> Self {
Self::new()
}
}