From the book "Rust in Action" (Manning, currently MEAP), the final example of Chapter 2 shows a way to implement a generic function that can work on BufRead + Sized
parameters:
fn process_lines<T: BufRead + Sized>(reader: T, re: Regex) {
for line_ in reader.lines() {
let line = line_.unwrap();
match re.find(&line) {
Some(_) => println!("{}", line),
None => (),
}
}
}
The code that uses it looks like:
let input = args.value_of("input").unwrap_or("-");
if input == "-" {
let stdin = io::stdin();
let reader = stdin.lock();
process_lines(reader, re);
} else {
let f = File::open(input).unwrap();
let reader = BufReader::new(f);
process_lines(reader, re);
}
I'm wondering if it is possible to declare the reader
variable earlier with a generic type so the process_lines() call can be factored out?
let reader: ????;
let input = args.value_of("input").unwrap_or("-");
if input == "-" {
let stdin = io::stdin();
reader = stdin.lock();
} else {
let f = File::open(input).unwrap();
reader = BufReader::new(f);
}
process_lines(reader, re);
Or by using an (anonymous) method that would return impl BufRead + Sized
?
Something like:
fn build_reader(input: &str) -> impl BufRead + Sized {
if input == "-" {
let stdin = io::stdin();
stdin.lock()
} else {
let f = File::open(input).unwrap();
BufReader::new(f)
}
}
But compiler is not very happy, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0e74d3e96de0bbf61b9f61d44d305e5e
Edit
I succeeded building a BufRead builder using the advice from https://stackoverflow.com/a/49964042/258622
fn build_reader(input: &str) -> Box<dyn BufRead> {
if input == "-" {
Box::new(BufReader::new(io::stdin()))
} else {
let f = File::open(input).unwrap();
Box::new(BufReader::new(f))
}
}
Compiler is happy, I wonder what are the consequences of not using stdin.lock()
...
Main now looks like:
fn main() {
...
let input = args.value_of("input").unwrap_or("-");
let reader = build_reader(input);
process_lines(reader, re)
}
Code that compiles: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c5218ef4a266597da6a36c21e060bcda
Edit 2 : A more idiomatic version taking advantage of the fact that clap::ArgMatches::value_of()
returns an Option<&str>
:
fn build_reader(input: Option<&str>) -> Box<dyn BufRead> {
match input {
None => Box::new(BufReader::new(io::stdin())),
Some(filename) => {
let f = File::open(filename).unwrap();
Box::new(BufReader::new(f))
}
}
}
fn main() {
...
let input = args.value_of("input");
let reader = build_reader(input);
process_lines(reader, re)
}
impl Trait
can only be one type at a time. They can't be chosen at runtime. – mcarton