Trait Object and Associated type with Sized


#1
pub struct Headers {
    data: LinkedHashMap<String, Box<Header>>,
}

pub trait HeaderX : Sized {
    fn header_name() -> &'static str;
    fn parse_header(raw: &Bytes) -> MqttResult<Self>;
}

pub trait Header {
    type X;
    fn fmt_header(&self, out: &mut BytesMut) -> MqttResult<()>;
}

with many implementations for the Header trait:

impl HeaderX for ConnectReturnCode {
    fn header_name() -> &'static str {
        "connect_return_code"
    }

    fn parse_header(raw: &Bytes) -> Result<ConnectReturnCode> {
    unimplemented!();
    }
}

impl Header for ConnectReturnCode {
type X = Self;
    fn fmt_header(&self, out: &mut BytesMut) -> Result<()> {
    unimplemented!();
    }
}

where each defines the type X = Self.

The first Problem is sized, which is not an allowed requirement for a trait object. If removing the trait bound of Sized, the constructor does not work anymore.
The second, the functions header_name and parse_header have no receiver.

This resulted in the idea of splitting the trait into two, one requering send, the other not.

How can I achieve a heterognous storage of items based on common traits and the elements having static functions (is there a better way to name them?).

Help much appreciated.


#2

Do you need Header to be a trait? Can you make an enum containing all the different header types?


#3

Tath was my first idea, but that does not really seem to be idiomatic.


#4

It’s completely idiomatic, when appropriate. If you know the complete set of header types you’ll have (i.e. it’s not something users of your library will come up with), then an enum is usually easier and more flexible to work with.


#5

Each of those headers blocks contain a few more fields plus each of those header fields has a determined encode and parse implementation. If use an enum, I end up using a struct with an enum which ends up being unergonomic.


#6

Hmm, I’m failing to see the lack of ergonomics from your (admittedly brief) description. Could you show something a bit concrete?

But back to your traits, you won’t be able to turn them into trait objects, as you’ve already discovered, as they stand. So if that’s the kind of API you’d like to have, more or less, you’re going to likely create a lot more unergonomic code than going with an enum :slight_smile:. The enum also has a much higher chance of getting more optimal code generated for it than trait objects, which might be a nice side-effect.


#7

I had another idea with a builder pattern incoroporated, but if that fails too I am going to go for Enums. Thanks for the (brief) discussion, much appreciated!