Beginner question about generic implementation

Greetings, I have encounter a problem when learning Rust.

Here is my code:

use flate2::bufread::GzDecoder;

enum Stream<R> {
    Uncompress(R),
    Gz(GzDecoder<R>)
}

struct MyReader<R> {
    inner: Stream<R>
}

impl<R: BufRead> MyReader<R> {
    fn new_from_path(path: &str) -> Self {
        let fp = File::open(path).unwrap();
        let fp = BufReader::new(fp);

        MyReader::new_from_stream(fp, false)
        // mismatched types
        // expected type parameter `R`
        // found struct `BufReader<File>`
    }

    fn new_from_stream(input_stream: R, is_compress: bool) -> Self {
        let inner = if is_compress {
            Stream::Gz(GzDecoder::new(input_stream))
        } else {
            Stream::Uncompress(input_stream)
        };

        MyReader { inner }
    }
}

I want to create MyReader that can open a file from path or from an existing stream, my question is why rust complains about mismatch types? According to the doc, BufReader<File> already implements BufRead trait.

How can I make this code working? Thanks in advance!

impl<R> MyReader<R> means that the code that’s calling any function inside the impl block gets to choose what R is, but your implementation of new_from_path always returns the specific type BufReader<File> in place of R. The caller could request a different type for R and your new_from_path implementation has no way to deal with that, which is why it’s a hard error.

You can fix this by making the impl specific rather than generic[1].

Playground

#![allow(dead_code)]
use flate2::bufread::GzDecoder;
use std::fs::File;
use std::io::{BufReader, BufRead};

enum Stream<R> {
    Uncompress(R),
    Gz(GzDecoder<R>)
}

struct MyReader<R> {
    inner: Stream<R>
}

impl MyReader<BufReader<File>> {
    fn new_from_path(path: &str) -> Self {
        let fp = File::open(path).unwrap();
        let fp = BufReader::new(fp);

        MyReader::new_from_stream(fp, false)
    }
}

impl<R: BufRead> MyReader<R> {
    fn new_from_stream(input_stream: R, is_compress: bool) -> Self {
        let inner = if is_compress {
            Stream::Gz(GzDecoder::new(input_stream))
        } else {
            Stream::Uncompress(input_stream)
        };

        MyReader { inner }
    }
}

new_from_stream is fine as it is, because the caller passes in the reader which is of type R, so the generic impl block works correctly there.


  1. technically you can leave the impl generic and just change the return type of new_from_stream but then the caller has to specify what R is even though it never gets used, which is annoying ↩︎

1 Like

Now it explains everything! Thanks~