Design decisions for storing trait objects


I'm creating a plugin system. The desired interface may look like this:

fn main() {
    let mars = Mars{};
    // Desired interface
    let plugin = Plugin::new("Mars")
        .provider(ProviderType::Air(mars)) // mars.clone()?

Now I'm stuck with how to store trait objects. I commented the code where I need help with design decision. Please excuse the fact that it's a bit contrary at a few points.

// Containers for example data
struct Food {
    glycemic_index: u32
struct Air {
    oxygen_level: u32

// Marker trait for storing itself in a container as trait objects.
// Or shall I use an enum?
trait Provider {}

// In the Plugin implementation I want to be able to call the traits' methods via matching.
// What's the best way here? Shall I store trait objects of type Provider or match over this enum?
enum ProviderType {
    Air(AirProvider), // Won't work like this

// Example Provider trait
trait FoodProvider: Provider {
    fn food(&self) -> Food;

// Another example Provider trait
trait AirProvider: Provider {
    fn air(&self) -> Air;

// Plugin = multiple Providers composed
struct Plugin {
    name: String,
    // What's the best way to store trait objects?
    //         Vec<Box<Provider>>       ?
    //         Vec<&'a (Provider + 'a)> ?
    //         Vec<ProviderType>        ?
    //         ...                      ?
    providers: Vec<ProviderType>,

// Apply builder pattern. See main() for desired interface
impl Plugin {
    fn new(name: &str) -> Self {
        Self {
            name: name.into(),
            providers: Vec::new(),

    // What's the best way to implement this function? Regarding function signature and body
    fn provider(mut self, provider: &ProviderType) -> Self {

// Specific Provider
struct Mars {}
impl Provider for Mars {}
impl AirProvider for Mars {
    fn air(&self) -> Air {
        Air {
            oxygen_level: 55
impl FoodProvider for Mars {
    fn food(&self) -> Food {
        Food {
            glycemic_index: 7

Thanks in advance :slight_smile:

trait Provider {}

is useless as it does not provide any method to get the actual interface from that. Just delete it.

enum ProviderType {
    Air(AirProvider), // Won't work like this

Of course it won't. The compiler does not know how the instances will look, so it can't reserve space for them. So you need to hold them by reference. You have a choice of:

  • A borrow, Air(&'a AirProvider). This requires a lift-time to restrict the life of the enum value to life-time of the plugin that provided it. Not sure how practical.
  • A unique owned instance, Air(Box<AirProvider>). You can borrow from this, but not extend the life-time.
  • A shared owned instance, Air(Rc<AirProvider>). You can hand out Rc<AirProvider> references that extend the life of the object.
  • A shared owned instance, thread-safe variant, Air(Arc<AirProvider>). Unlike the previous, can be shared between threads.

Which you use is up to you, depending on what you need.

Note that since the *Provider traits need to be object-safe, they can't be generic (except over lifetimes), so if you need to return derived objects from them, you'll need boxes (or rcs) there too.

Ah thanks @jan_hudec. Thanks for listing my choices.
I also asked some questions on the IRC. I definitely have to specify the type right away.
I'd like to do:

enum ProviderType<T: Clone> {

but it turns out Rust doesn't have higher-kinded types yet.
So I tried to create a naive workaround:

type Container = Rc;
enum ProviderType {

but Rc requires a <type>. This would have had the advantage that I can just change Container to whatever I like when I change my mind.
I really dislike the idea that I have to hardcode the type already.

Box<Trait> is a specific type. Try:

type Container<T> = Rc<Box<T>>;

Thanks, kornel, that worked :slight_smile: