enum Colour {
Red(Red),
Green(Green),
Blue(Blue),
}
impl Shine for Colour {
fn shine(&self) {
use Colour::*;
match self {
Red(c) => c.shine(),
Green(c) => c.shine(),
Blue(c) => c.shine(),
}
}
}
struct Car {
colour: Colour,
}
struct Dealership {
cars: Vec<Car>, // No longer have to match type to add to correct Vec
}
This avoids dynamic dispatch, at the expense of a match and resulting boilerplate in Colour, and possible memory wastage in the Vec<> if your Shines are different sizes. If your colours are a closed set, then the compiler will help you ensure code validity using this method. [On the other hand, if the API is meant to be extensible (users can add their own Shines), then dynamic dispatch via Box<dyn Shine> is the best option.]
You could probably make such an API cleaner to use by implementing From<Red> etc. for Colour, and writing taking colour: impl Into<Colour> in function parameters.
I recon that you are right and that it is better to embed that information into the type system, in the end however, Kestrer's answer inspired my solution.