在Rust中向文件或标准输出(stdout)写入数据

17
我正在学习Rust,但遇到了一些困难。
我想给用户提供将输出写入标准输出或提供的文件名的选项。
我从这里找到了使用extra::getopts的示例代码:here。在do_work函数中,我尝试做到这一点:
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" ) );
}

但是它输出以下错误:
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 {
                            ^~~~~~~~~~~~~~~~~~~

但是我不明白问题出在哪里,因为FileStdWriter都实现了Writer特质。有人能解释一下我错在哪了吗?
谢谢!
2个回答

24

自2014年以来,Rust发生了很多变化,所以这里是一个适用于使用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>,
};

这与@Arjan的答案几乎相同,只是~被替换为Box,并且一些名称已更改。我省略了BufferedWriter,但如果您需要它,我相信现在被称为BufWriter


1
let out_writer: Box<Write> = ... Box::new(io::stdout()),(不需要 as ...)在1.16版本中也可以工作。我不知道哪种更符合惯用法。 - mkjeldsen

13

是的,两者都实现了Write,但问题在于BufWriter期望一个实现Writer的类型T,而T不能同时是FileStdout

你必须将两个类型强制转换为公共类型(可以是Box<dyn Write>&dyn Write,但由于你无法返回引用,所以必须使用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();
}

您还应该正确处理错误,而不仅仅使用unwrap(在示例中仅为简单起见)。


1
哦,原来如此。我不知道你必须明确地声明它作为你想要的 Trait 。非常感谢! - Neil
1
重点是将其转换为通用类型,而不是该类型是 T 需要的特征(它可以是实现 io::Writer 的其他内容)。 - Arjan
6
自本回答撰写以来,Rust已经移除了波浪线(~)符号,并引入了一些有关DST的更改。希望进行更新。 - helios35

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接