9

I'm trying to type-pun an io::stdio instance and a BufReader wrapping a file so that I can write code assuming a buffered input source. I've tried several variations of trying to cast an io::stdio() to a BufRead type and all have failed with one or more variations of:

error: mismatched types: expected `std::io::stdio::StdinLock<'_>`, found `std::io::buffered::BufReader<std::fs::File>` (expected struct `std::io::stdio::StdinLock`, found struct `std::io::buffered::BufReader`) [E0308] csv_to_json.rs:26 reader = BufReader::new(file.unwrap()); 

Here's the code:

use std::io; use std::io::BufReader; use std::io::prelude::*; use std::env; use std::process::exit; use std::fs::File; fn usage() { println!("Usage: cat input.csv | csv_to_json"); } fn main() { let stdin = io::stdin(); let mut reader = stdin.lock(); // Assignment. (1) let args: Vec<String> = env::args().collect(); if args.len() > 1 { usage(); exit(1); } else if args.len() == 1 { let file = File::open(args[0].clone()); if !file.is_ok() { usage(); exit(1); } reader = BufReader::new(file.unwrap()); // Reassignment. This is the line that fails. } // Rest of the code works with reader assuming a buffered input source. } 

Things that I have tried:

let mut reader : BufRead = io::stdin(); // Fails. let mut reader : BufReader<Read> = io::stdin(); // Fails. let mut reader : BufReader<_> = io::stdin(); // Fails let mut reader : BufRead = io::stdin().lock(); // Fails later. let mut reader = BufReader::new(io::stdin()); // Fails with type mismatch. let mut reader = BufReader::new(io::stdin().lock()); // Fails with type mismatch. 

I've even tried extracting that code to its own function and the return type signature fails with similar error conditions. How can I create a "reader" variable that's either a BufferedReader over stdio or a BufferedReader over a File without facing type errors everywhere?

2

2 Answers 2

8

Your code can be rewritten so that there is no need to reassign reader variable (see @Vladimir Matveev answer).

If you really need it, then reader variable must be a trait object:

let mut bufread;//must be defined before reader to avoid lifetime issues let stdin = io::stdin(); let mut reader = &mut stdin.lock() as &mut BufRead; // Assignment. (1) //... bufread = BufReader::new(file.unwrap());//store BufReader value reader = &mut bufread;// reassign BufRead reference 

Boxed form of trait object allows to avoid additional variable bufread:

let stdin = io::stdin(); let mut reader = Box::new(stdin.lock()) as Box<BufRead>; //... reader = Box::new(BufReader::new(file.unwrap())); 
Sign up to request clarification or add additional context in comments.

2 Comments

An &BufRead won't be very helpful though, since all the interesting BufRead methods take &mut self.
I'd like to note that there is no need to declare any of the variables before the condition because they are not used in it. So it is better to rewrite the code to avoid trait objects as they are really unnecessary here.
4

This will work.

use std::io; use std::io::BufReader; use std::io::prelude::*; use std::env; use std::process::exit; use std::fs::File; fn usage() { println!("Usage: cat input.csv | csv_to_json"); } fn main() { let stdin = io::stdin(); let mut reader = Box::new(stdin.lock()) as Box<BufRead>; let args: Vec<String> = env::args().collect(); if args.len() > 1 { usage(); exit(1); } else if args.len() == 1 { let file = File::open(args[0].clone()); if !file.is_ok() { usage(); exit(1); } reader = Box::new(BufReader::new(file.unwrap())); } } 

1 Comment

Thanks for pointing to the correct use of Box in such a scenario. Very helpful.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.