Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing to a file or stdout in Rust

Tags:

file

rust

stdio

I'm learning Rust, and I'm somewhat stumped.

I'm trying to give the user the option of writing output to stdout or to a supplied filename.

I started with the example code that's given for using extra::getopts located here. From there, in the do_work function, I'm trying to do this:

use std::io::stdio::stdout;
use std::io::buffered::BufferedWriter;

fn do_work( input: &str, out: Option<~str> ) {
    println!( "Input:  {}", input );
    println!( "Output: {}", match out {
        Some(x) => x,
        None    => ~"Using stdout"
    } );
    let out_writer = BufferedWriter::new( match out {
        // I know that unwrap is frowned upon, 
        // but for now I don't want to deal with the Option.
        Some(x) => File::create( &Path::new( x ) ).unwrap(),
        None    => stdout()
    } );
    out_writer.write( bytes!( "Test output\n" ) );
}

But it outputs the following error:

test.rs:25:43: 28:6 error: match arms have incompatible types: expected `std::io::fs::File` but found `std::io::stdio::StdWriter` (expected struct std::io::fs::File but found struct std::io::stdio::StdWriter)
test.rs:25     let out_writer = BufferedWriter::new( match out {
test.rs:26         Some(x) => File::create( &Path::new( x ) ).unwrap(),
test.rs:27         None    => stdout()
test.rs:28     } );
test.rs:25:22: 25:41 error: failed to find an implementation of trait std::io::Writer for [type error]
test.rs:25     let out_writer = BufferedWriter::new( match out {
                            ^~~~~~~~~~~~~~~~~~~

But I don't understand what the issue is because both File and StdWriter implement the Writer Trait. Can someone explain what I'm doing wrong?

Thanks!

like image 304
Neil Avatar asked Mar 12 '14 14:03

Neil


2 Answers

Yes, both implement Write, but the problem is BufWriter is expecting a type T that implements Writer, and that T can't be File and Stdout at the same time.

You must cast both to the common type (either Box<dyn Write> or &dyn Write, but since you cannot return references you have to use Box):

fn do_work(input: &str, out: Option<String>) {
    let mut out_writer: Box<dyn Write> = BufWriter::new(match out {
        Some(ref x) => Box::new(File::create(&Path::new(x)).unwrap()),
        None => Box::new(stdout()),
    });
    out_writer.write(b"Test output\n").unwrap();
}

You should also handle errors properly, not just using unwrap (used in example for simplicity).

like image 153
Arjan Avatar answered Oct 04 '22 02:10

Arjan


A lot has changed in Rust since 2014, so here is an answer that works for me using Rust 1.15.1:

let out_writer = match out {
    Some(x) => {
        let path = Path::new(x);
        Box::new(File::create(&path).unwrap()) as Box<dyn Write>
    }
    None => Box::new(io::stdout()) as Box<dyn Write>,
};

This is pretty much the same as @Arjan's answer, except that ~ was replaced by Box, and some names have changed. I'm leaving out BufferedWriter, but if you want that, I believe it is now named BufWriter.

like image 45
Paul A Jungwirth Avatar answered Oct 04 '22 02:10

Paul A Jungwirth