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.