Structs owning a trait


#1

I’m writing some code that deals with file formats that are basically nested. I’d like this to work with a Reader so that is generic where the input comes from.

So far, my outermost wrapper is defined like:

struct Outer<'a> {
    reader: &'a mut (io::Reader+'a),
    ...
}

with a constructor signature like:

impl<'a> Outer<'a> {
    put fn new<'b>(mut rd: &'b mut (io::Reader+'b)) -> MyResult> { ... }
}

this does seem to largely work, but the signatures get a little awkward. My Outer struct would then implement some other trait which would be embedded in a similar manner in some other struct that implements the next layer inside the file format onion.

My question: is this the best way to do this. It does allow the user some flexible in how everything is allocated, even though the thing that is the Reader can’t really be used any more.

Would I be better off embedding the trait item, and making it a parameter of the Outer class. Something like:

struct Outer<'a, T> where T: io::Reader+'a+Sized {
    reader: T,
    ...
}

#2

I don’t understand why you feel the need to use another lifetime besides 'a. What I expected to see instead was

impl<'a> Outer<'a> {
    pub fn new(r: &'a mut (io::Reader+'a)) -> MyResult<Outer<'a>> {
        …
    }
}

On whether it is the best way: I don’t think it is. I would prefer to go “full generic” like this:

struct Outer<R> {
    reader: R,
    …
}

impl<R: Reader> Outer<R> {
    pub fn new(r: R) -> MyResult<R> {
        …
    }
}

This makes things a lot simpler and more flexible because you can later decide on a case-by-case basis what R should be. This way you don’t enforce type erasure (but it’s still possible). And you don’t enforce references (but it’s still possible). The possibilities for such an R include:

  • SomeSpecificReader
  • &mut SomeSpecificReader
  • Box<Reader>
  • &mut Reader

since all these types also implement the Reader trait. :slight_smile:

HTH