I'm trying to implement a "polymorphic" Input enum which hides whether we're reading from a file or from a stdin. More concretely, I'm trying build an enum that will have a lines method that will in turn "delegate" that call to either a File wrapped into a BufReader or to a StdInLock (both of which have the lines() method).
Here's the enum:
enum Input<'a> { Console(std::io::StdinLock<'a>), File(std::io::BufReader<std::fs::File>) } I have three methods:
from_argfor deciding whether we're reading from a file or from a stdin by checking whether an argument (filename) was provided,filefor wrapping a file with aBufReader,consolefor locking the stdin.
The implementation:
impl<'a> Input<'a> { fn console() -> Input<'a> { Input::Console(io::stdin().lock()) } fn file(path: String) -> io::Result<Input<'a>> { match File::open(path) { Ok(file) => Ok(Input::File(std::io::BufReader::new(file))), Err(_) => panic!("kita"), } } fn from_arg(arg: Option<String>) -> io::Result<Input<'a>> { Ok(match arg { None => Input::console(), Some(path) => try!(Input::file(path)), }) } } As far as I understand, I have to implement both BufRead and Read traits for this to work. This is my attempt:
impl<'a> io::Read for Input<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { match *self { Input::Console(ref mut c) => c.read(buf), Input::File(ref mut f) => f.read(buf), } } } impl<'a> io::BufRead for Input<'a> { fn lines(self) -> Lines<Self> { match self { Input::Console(ref c) => c.lines(), Input::File(ref f) => f.lines(), } } fn consume(&mut self, amt: usize) { match *self { Input::Console(ref mut c) => c.consume(amt), Input::File(ref mut f) => f.consume(amt), } } fn fill_buf(&mut self) -> io::Result<&[u8]> { match *self { Input::Console(ref mut c) => c.fill_buf(), Input::File(ref mut f) => f.fill_buf(), } } } Finally, the invocation:
fn load_input<'a>() -> io::Result<Input<'a>> { Ok(try!(Input::from_arg(env::args().skip(1).next()))) } fn main() { let mut input = match load_input() { Ok(input) => input, Err(error) => panic!("Failed: {}", error), }; for line in input.lines() { /* do stuff */ } } Complete example in the playground
The compiler tells me that I'm pattern matching wrongly and that I have mismatched types:
error[E0308]: match arms have incompatible types --> src/main.rs:41:9 | 41 | / match self { 42 | | Input::Console(ref c) => c.lines(), | | --------- match arm with an incompatible type 43 | | Input::File(ref f) => f.lines(), 44 | | } | |_________^ expected enum `Input`, found struct `std::io::StdinLock` | = note: expected type `std::io::Lines<Input<'a>>` found type `std::io::Lines<std::io::StdinLock<'_>>` I tried to satisfy it with:
match self { Input::Console(std::io::StdinLock(ref c)) => c.lines(), Input::File(std::io::BufReader(ref f)) => f.lines(), } ... but that doesn't work either.
I'm really out of my depth here, it seems.
StdinLockcontains a reference to aStdinobject.