Recovering an object embedded in a trait object


#1

I’m working on a packet processor. Some of the packets are containers. A container packet can contain any other valid packet (including itself). So, an encrypted packet could contain a compressed packet, which could contain some data. Also, a signed container could container another signed container. Here’s the idea:

[ compressed packet: [ data packet ], another packet, ... ]

To implement the various containers, I create a pipeline. At the bottom is, for instance, a file. When I encounter a compressed packet, I push a compression filter on the front of the pipeline, and read from that to get the packets contained in the compression packet. When I’ve read all of the packets from that container, I need to pop the compression filter to process any packets following the container:

[ decompressor ] -- owns --> [ decryptor ] -- owns --> [ file ]

This construct combined with this recursive definition means that I can’t use Rust’s generic types, because the compiler can’t identify a base case for the recursion (a compressed packet could contain a compressed packet, etc.). The consequence is that I have to use trait objects to erase each filter’s type. Specifically, when I push a filter on the pipeline, I cast it to a trait object.

For various reasons, my filters implement not only the io::Read trait, but also a custom trait. It is essential that I recover the original object. But, when using flate2’s high level API, I need to cast the decompressor to an io::Read (because it doesn’t implement my Filter). Now, I can’t recover the underlying object due to type erasure!

The only solution that I currently see is to implement the Filter trait for each compression algorithm, etc. This is a lot of boilerplate code that I’d prefer to avoid. I’m hoping that there is a better way that I’ve overlooked.

Thanks!

Sample code


#2

Maybe use Rc<RefCell<>> and wrap one copy in struct you can implement the trait on.


#3

I’m not sure what you’re imagining by “implement the Filter trait for each compression algorithm”, but this is not a lot of boilerplate:

impl<'f> Filter for flate2::read::DeflateDecoder<Box<Filter + 'f>> {
    fn into_inner<'a>(self: Box<Self>) -> Option<Box<Filter + 'a>> where Self: 'a {
        Some((*self).into_inner())
    }
}

After all, the into_inner method on DeflateDecoder is an inherent method, not part of any trait; you need to somehow let the compiler know that that’s what you want to call as part of your generic setup. (In the above code, the into_inner in the function body refers to that inherent method. Be careful, because if you change the types, the compiler might decide that you wanted to call Filter::into_inner, i.e. the very method you’re implementing… leading to strange-looking errors.) There are other possible approaches but the above seems like the simplest one.

Then you can get rid of GenericFilter and use DeflateDecoder directly as a Filter.

edit: fixed lifetimes