Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing BufRead trait compatibility between io::stdio and BufReader

Tags:

rust

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?

like image 818
Divye Kapoor Avatar asked Mar 19 '16 19:03

Divye Kapoor


2 Answers

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()));  
like image 196
aSpex Avatar answered Nov 15 '22 09:11

aSpex


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()));
    }
}
like image 41
A.B. Avatar answered Nov 15 '22 09:11

A.B.