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