将错误消息写入 Standard Error 而不是 Standard Output


目前,我们正在使用 println!宏。在大多数终端中,有两种输出:标准 输出stdout) 用于一般信息,标准错误stderr) 用于错误消息。这种区别使用户能够选择将程序的成功输出定向到文件,但仍将错误消息打印到屏幕上。


println! 宏只能打印到标准输出,所以我们得用别的东西来打印到标准错误。


检查写入错误的位置


首先,让我们观察一下 minigrep 打印的内容当前是如何写入 standard output 的,包括我们想要写入 standard error 的任何错误消息。我们将通过将标准输出流重定向到文件来做到这一点,同时故意导致错误。我们不会重定向标准错误流,因此发送到标准错误的任何内容都将继续显示在屏幕上。


命令行程序应将错误消息发送到标准错误流,因此即使我们将标准输出流重定向到文件,我们仍然可以在屏幕上看到错误消息。我们的程序目前表现不佳:我们即将看到它将错误消息输出保存到文件中!


为了演示此行为,我们将使用 > 和文件路径 output.txt,我们要将标准输出流重定向到该流。我们不会传递任何参数,这应该会导致错误:


$ 货物运行 > output.txt


> 语法告诉 shell 将标准输出的内容写入 output.txt而不是屏幕。我们没有看到我们期望打印到屏幕上的错误消息,因此这意味着它一定已经结束在文件中。以下是output.txt包含的内容:

Problem parsing arguments: not enough arguments


是的,我们的错误消息正在打印到标准输出。将此类错误消息打印为 standard error 更有用,因此只有成功运行的数据才会出现在文件中。我们会改变这种情况。


打印错误到标准错误


我们将使用示例 12-24 中的代码来更改错误消息的打印方式。由于我们在本章前面进行了重构,因此所有打印错误消息的代码都在一个函数 main 中。标准库提供了打印到标准错误流的 eprintln! 宏,因此让我们将调用 println! 的两个位置更改为打印错误,以使用 eprintln! 相反。


文件名: src/main.rs
use std::env;
use std::process;

use minigrep::Config;

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::build(&args).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {err}");
        process::exit(1);
    });

    if let Err(e) = minigrep::run(config) {
        eprintln!("Application error: {e}");
        process::exit(1);
    }
}

示例 12-24:使用 eprintln 将错误消息写入 standard error 而不是 standard 输出!


现在让我们以相同的方式再次运行程序,不带任何参数,并使用 > 重定向标准输出:

$ cargo run > output.txt
Problem parsing arguments: not enough arguments


现在我们在屏幕上看到错误output.txt并且不包含任何内容,这是我们期望命令行程序的行为。


让我们使用不会导致错误但仍将标准输出重定向到文件的参数再次运行程序,如下所示:

$ cargo run -- to poem.txt > output.txt


我们不会看到终端的任何输出,output.txt 将包含我们的结果:


文件名: output.txt

Are you nobody, too?
How dreary to be somebody!


这表明我们现在使用标准输出来成功输出,并根据需要使用标准误差来表示错误输出。


总结


本章回顾了你到目前为止学到的一些主要概念,并介绍了如何在 Rust 中执行常见的 I/O作。通过使用命令行参数、文件、环境变量和用于打印错误的 eprintln! 宏,您现在可以编写命令行应用程序了。结合前几章中的概念,您的代码将组织良好,将数据有效地存储在适当的数据结构中,很好地处理错误,并得到良好的测试。


接下来,我们将探索一些受函数式语言影响的 Rust 特性:闭包和迭代器。