Function which returns a BufReader<R>, R not known at compilation

Hi all,

I want to create a function which returns a BufReader<R>, but R is not known at compile time. More precisely, R could be a File, a GzDecoder<File> or any other compression decoder.

I guess returning an impl Trait won't work. So what should I use ? Trait objects ?

Thanks for your help.

There are two options. The simplest is Box<dyn Read>, but another option would be an enum:

enum MyIOType {
    File(File),
    Gzip(GzDecoder<File>),
}

impl Read for MyIOType {
    ...
}

@alice Thanks for your suggestions. I can see with enum but for the trait object, its not clear to me. Is it just a matter of Box<dyn Read> as the return type from the function ?

Yes, just use Box<dyn Read> instead of R.

Thanks @alice

I can return a Box<syn Read> but I can't figure out how to use it with regular BufReader methods:

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub enum CompressionScheme {
    Gzip,
    Bzip2,
    Xz,
    NotCompressed,
}

impl CompressionScheme {
    /// True if this matches any of the valid compression scheme
    #[inline(always)]
    pub fn is_compressed(&self) -> bool {
        self != &CompressionScheme::NotCompressed
    }

    fn reader(&self, path: &PathBuf) -> Box<dyn Read> {
        // open target file
        let file = File::open(&path).expect("unable to open file");

        // if file is compressed, we need to call a specific reader
        if self.is_compressed() {
            let decoder = GzDecoder::new(file);
            let reader = BufReader::new(decoder);
            Box::new(reader)
        } else {
            let reader = BufReader::new(file);
            Box::new(reader)
        }
    }
}

Any hint ?

Sorry, I discovered it's really easy to use it a a reader, just replacing Box<dyn Read> by Box<dyn BufRead>.

Anyway, thanks a lot for your help.

Ah, yeah, you would want either BufReader<Box<dyn Read>> or Box<dyn BufRead>.

Hi,

Now I can use my new reader with any of the methods implemented by BufRead. But if I want to implement another trait for this reader, I can do:

trait Foo {
    fn set_offset(&self) -> String;
}

impl Foo for dyn BufRead {
    fn set_offset(&self) -> String {
        "from set_offset()".to_string()
    }
}

but how to specialize this function depending on the underlying type ?

Thanks for your help.

you can use

trait Foo: BufRead {
    fn set_offset(&self) -> String;
}

and then use Box<dyn Foo> instead of Box<dyn BufRead>

@juggle-tux Thanks for your suggestion.

In the meantime, I moved to an enum which keeps track of the BufReader. But I'll try what you suggest.