What you are trying to achieve is possible, but you might need to structure it slightly differently.
The code you've written tries to treat a struct's name as some sort of object that can be passed around and used to construct the type dynamically, similar to the java.lang.Class
in Java or the name of a class in Python.
Rust deliberately doesn't have a reflection system which knows how to construct objects at runtime, so we kinda need to implement it ourselves. This isn't as hard as it sounds.
What I would do is store a list of functions that can be used to construct these values, and make sure the function returns a value who's type is erased at runtime. Normally you can't store a Baz
and a Bar
in the same slice because they have different types with possibly different sizes, so it's important to add some level of indirection to "forget" the actual size/type of Bar
and Baz
(i.e. Box<dyn Foo>
).
First, let's modify the Foo
trait so it is "object safe". In order to erase the type at runtime, your trait can't have any methods which would require knowing a value's actual type, so things like associated constants/types (they're associated with your type) or methods that accept/return Self
by value (the compiler would need to know the value's type in order to allocate the right amount of stack space for variables) are out.
trait Foo {
fn name(&self) -> &'static str;
}
struct Bar;
impl Foo for Bar { fn name(&self) -> &'static str { "Bar" } }
struct Baz;
impl Foo for Baz { fn name(&self) -> &'static str { "Baz" } }
Next, we add our constructor functions to the structs
array.
type FooConstructor = fn() -> Box<dyn Foo>;
let structs: [FooConstructor; 2] = [
|| Box::new(Bar),
|| Box::new(Baz),
];
And now we can iterate through the constructors, and create instances of our Foo
type.
for s in structs {
let instance: Box<dyn Foo> = s();
println!("{}", instance.name());
}
(playground)
This way of passing around a closure to "defer" an object's construction can be particularly useful when you are making some sort of "factory" that will pick and choose which Foo
implementation to construct at runtime.