This is the simplest solution but will borrow and lock Stdin.
use std::fs::File; use std::io::{self, ReadBufRead, BufReadRead}; use std::fs::File; struct Input<'a> { source: Box<BufRead + 'a>, } impl<'a> Input<'a> { fn console(stdin: &'a io::Stdin) -> Input<'a> { Input { source: Box::new(stdin.lock()) }, } } fn file(path: &str) -> io::Result<Input<'a>> { File::open(path) .map(|file| Input { .map(|file| Input { source: Box::new(io::BufReader::new(file)), }) } } impl<'a> Read for Input<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.source.read(buf) } } impl<'a> BufRead for Input<'a> { fn fill_buf(&mut self) -> io::Result<&[u8]> { self.source.fill_buf() } fn consume(&mut self, amt: usize) { self.source.consume(amt); } } Due to default trait methods, Read and BufRead are fully implemented for Input. So you can call lines on Input.
let input = Input::file("foo.txt").unwrap(); for line in input.lines() { println!("input line: {:?}", line); }