Static factory method for trait
In Rust, every variable must be a single specific type. This is different to OO languages where a variable can have a type Foo
, and that means that the variable contains a Foo
, or any subclass of Foo
.
Rust doesn't support this. If a variable has a type Foo
, it must contain a Foo
(ignoring any unsafe concerns).
Rust also doesn't support using traits as types (without the dyn
keyword).
In your example, you have:
trait Abstract {
fn new(name: &str) -> Self {
// ...
}
}
The return type Self
here means "whatever type this trait is being implemented on". However, by providing a body in the trait definition, you're providing a default implementation, so this function should in theory apply to any type, and so the compiler has no information about the real concrete type Self
, and therefore the Sized
bound it not met (which in practice is very restrictive and probably not what you want).
If you want a function that takes a string and returns "some type T
that implements Abstract
", you could use a "trait object", which would look roughly like:
// outside trait definition
fn new_abstract(name: &str) -> Box<dyn Abstract> { // dyn keyword opts into dynamic dispatch with vtables
match name {
"foo" => Box::new(Foo {}),
// ...
}
}
However, I'd warn against this pattern. Dynamic dispatch has some runtime overhead, and prevents many compile-time optimizations. Instead, there may be a more "rusty" way of doing it, but it's hard to tell without more context.
In general, constructing a type based on the value of a string smells like a bit of an anti-pattern.
You can consider using an enum
instead of dynamically dispatch it:
trait Abstract {
fn name(&self) -> &str;
fn new(name: &str) -> Option<AbstractVariant> {
match name {
"foo" => Some(AbstractVariant::Foo(Foo {})),
"bar" => Some(AbstractVariant::Bar(Bar {})),
"baz" => Some(AbstractVariant::Baz(Baz {})),
_ => None,
}
}
}
enum AbstractVariant {
Foo(Foo),
Bar(Bar),
Baz(Baz),
}
Playground