[Rust Guide]12.8. Writing Error Messages to Standard Error
[Rust Guide] 12.8. Writing Error Messages to Standard Error
[Rust 指南] 12.8. 将错误信息写入标准错误
12.8.0 Before We Begin
12.8.0 在开始之前
Chapter 12 builds a sample project: a command-line program. The program is grep (Global Regular Expression Print), a tool for global regular-expression searching and output. Its function is to search for specified text in a specified file. This project is divided into these steps: 第 12 章构建了一个示例项目:一个命令行程序。该程序是 grep (Global Regular Expression Print),一个用于全局正则表达式搜索和输出的工具。它的功能是在指定文件中搜索指定的文本。该项目分为以下几个步骤:
- Receiving command-line arguments
- 接收命令行参数
- Reading files
- 读取文件
- Refactoring: improving modules and error handling
- 重构:改进模块和错误处理
- Using TDD (test-driven development) to develop library functionality
- 使用 TDD(测试驱动开发)来开发库功能
- Using environment variables
- 使用环境变量
- Writing error messages to standard error instead of standard output (this article)
- 将错误信息写入标准错误而不是标准输出(本文)
If you find this helpful, please like, bookmark, and follow. To keep learning along, follow this series. 如果您觉得有帮助,请点赞、收藏并关注。要继续学习,请关注此系列。
12.8.1 Review
12.8.1 回顾
Here is all the code written up to the previous article. 以下是截至上一篇文章所编写的所有代码。
lib.rs:
use std::error::Error;
use std::fs;
pub struct Config {
pub query: String,
pub filename: String,
pub case_sensitive: bool,
}
impl Config {
pub fn new(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("Not enough arguments");
}
let query = args[1].clone();
let filename = args[2].clone();
let case_sensitive = std::env::var("IGNORE_CASE").is_err();
Ok(Config { query, filename, case_sensitive, })
}
}
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.filename)?;
let results = if config.case_sensitive {
search(&config.query, &contents)
} else {
search_case_insensitive(&config.query, &contents)
};
for line in results {
println!("{}", line);
}
Ok(())
}
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let mut results = Vec::new();
for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
}
results
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let mut results = Vec::new();
let query = query.to_lowercase();
for line in contents.lines() {
if line.to_lowercase().contains(&query) {
results.push(line);
}
}
results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn case_sensitive() {
let query = "duct";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";
assert_eq!(vec!["safe, fast, productive."], search(query, contents));
}
#[test]
fn case_insensitive() {
let query = "rUsT";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
assert_eq!(
vec!["Rust:", "Trust me."],
search_case_insensitive(query, contents)
);
}
}
main.rs:
use std::env;
use std::process;
use minigrep::Config;
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::new(&args).unwrap_or_else(|err| {
println!("Problem parsing arguments: {}", err);
process::exit(1);
});
if let Err(e) = minigrep::run(config) {
println!("Application error: {}", e);
process::exit(1);
}
}
12.8.2 Standard Output vs. Standard Error
12.8.2 标准输出与标准错误
The current code prints all information, including error messages, to the terminal. Most terminals provide two output streams: standard output (stdout) and standard error (stderr). General information should go to standard output, while error messages should go to standard error. 当前代码将所有信息(包括错误信息)打印到终端。大多数终端提供两个输出流:标准输出 (stdout) 和标准错误 (stderr)。一般信息应发送到标准输出,而错误信息应发送到标准错误。
The advantage of this separation is that normal output can be redirected into a file while error messages still appear on the screen. The println! macro can only print to standard output. The eprintln! macro can print to standard error.
这种分离的好处是,正常输出可以重定向到文件中,而错误信息仍然会显示在屏幕上。println! 宏只能打印到标准输出,而 eprintln! 宏可以打印到标准错误。
Using the current code, run this command in the terminal:
使用当前代码,在终端中运行此命令:
cargo run > output.txt
This redirects output to output.txt, but the command does not include any arguments, so the program should error. Because the error messages are also written to standard output, they end up in output.txt. A better approach is to print error messages to standard error, which keeps standard output clean and separate from errors. 这会将输出重定向到 output.txt,但该命令不包含任何参数,因此程序应该报错。由于错误信息也被写入标准输出,它们最终会出现在 output.txt 中。更好的方法是将错误信息打印到标准错误,这样可以保持标准输出的整洁,并将其与错误分离开来。
12.8.3 Modifying the Code
12.8.3 修改代码
Changing the code so that error messages go to standard error is quite simple. We only need to change all error printing from println! to eprintln!. Because all error handling is in main.rs, we only need a small change there, and lib.rs does not need to be modified at all:
将代码修改为将错误信息发送到标准错误非常简单。我们只需要将所有错误打印从 println! 改为 eprintln!。由于所有的错误处理都在 main.rs 中,我们只需要在那里做一点小改动,而 lib.rs 完全不需要修改:
main.rs:
use std::env;
use std::process;
use minigrep::Config;
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::new(&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);
}
}
Now run the previous command again. It still has no arguments, so the program errors, but this time it will not put the error message into output.txt; instead, it will print directly in the terminal: 现在再次运行之前的命令。它仍然没有参数,所以程序报错,但这次它不会将错误信息放入 output.txt;相反,它会直接在终端中打印:
$ cargo run > output.txt
Problem parsing arguments: not enough arguments
Then try a normal run with arguments: 然后尝试带参数的正常运行:
$ cargo run -- to poem.txt > output.txt
The output is redirected to output.txt. Open it: 输出被重定向到 output.txt。打开它:
Are you nobody, too?
How dreary to be somebody!
That is the result we want: errors are printed directly in the terminal, while normal output is redirected into the file. 这就是我们想要的结果:错误直接打印在终端中,而正常输出被重定向到文件中。