How can I assign a value to a variable where the type of a generic trait is not known until runtime?

39 Views Asked by At

Apologies in advance, this is probably a trivial question, but I don't even know what to search for an answer. I want to read files that might or might not be zip compressed. Depending on the file extension, I need to either create a Reader from a simple io::File or from a BGZFReader<File>. Reader will be happy with anything that implements Read+Seek. The - non-working - code should look something like this:

let file_reader = if filename.as_ref().extension().unwrap_or_default() == "gz"
    {   
        let in_file = BGZFReader::new(File::open(filename)?)?;
        Reader::new(in_file)
    }
    else
    {
        let in_file = File::open(filename)?;
        Reader::new(in_file)
    };

But of course this doesn't work, because Reader<File> is not the same as Reader<BGZFReader<File>>. Is there a way to say that my variable file_reader can be any instance of Reader, irrespective of what exact implementation of Read+Seek I'm using?

The reason I want to define file_reader generically is because there are multiple files I need to open and then pass to the downstream code. Each could be compressed or not, so writing if/else statements for all possible combinations would become unmanageable very quickly.

1

There are 1 best solutions below

0
DaGaMs On

Thanks to hints in the comments, I came up with this solution:

trait ReadAndSeek: Read+Seek {}
impl <R: Read+Seek> ReadAndSeek for R {}

fn open_file<P: AsRef<Path> + Debug>(path: P) -> Result<Reader<Box<dyn ReadAndSeek>>>
{
    if path.as_ref().extension().unwrap_or_default() == "gz"
    {
        let in_file = BGZFReader::new(File::open(path)?)?;
        Ok(Reader::new(Box::new(in_file)))
    }
    else
    {
        let in_file = File::open(path)?;
        Ok(Reader::new(Box::new(in_file)))
    }
}

and downstream:

let read_file = open_file(read_bed)?;
let coord_file = open_file(coord_bed)?;

let processor = Processor::new(read_file, coord_file)?;

The two things I didn't understand initially were:

  1. Creating a compound trait as described here
  2. Boxing the inner file reader with a dyn trait