Search code examples
rustpolymorphismtraits

Polymorphism in trait objects with associated types


I have a trait with a number of implementations that satisfy the common interface:

pub trait Schema<'a> {
    // ConfigTypes will be parsed from HTTP POST requests, but different
    // implementations can have different fields and know how to parse
    // themselves
    type ConfigType: rocket::request::FromForm<'a>
    fn configure(&mut self, cfg: Self::Configure);
    // and more...
}

Now imagine I want to implement a simple key/value store to map any number of these schemas from arbitrary strings. I feel like this should be possible with a HashMap, but what would the type be?

use std::collections::HashMap;

struct Datastore<'a> {
    cache: HashMap<String, dyn Schema<'b>>
}

This fails complaining that the associated type ConfigType must be specified, but I want to store any implementation of Schema behind this cache!

I expected the problem is in irregular sizing, since the values of HashMap are expected to have static size. Alright then -- let's add Boxes to things.

// as above
struct Datastore<'a> {
    cache: HashMap<String, Box<dyn Schema<'b>>>
}

but alas -- the same error presents. How do I define this structure to be able to hold any Schema implementer?


Solution

  • The associated type must be known. Otherwise how does the compiler know what type to use when you call methods that use that type?

    There are a few ways around this:

    1. You use Box<dyn Schema<ConfigType = &dyn rocket::request::FromForm<'b>, 'b> You might have to implement rocket::request::FromForm<'b> for &dyn rocket::request::FromForm<'b> so it can downcast and pass it to the correct type.
    2. You just erase the whole type, and use Box<dyn Any>. Now you have to downcast it before you can use it.