148 lines
8.1 KiB
HTML
148 lines
8.1 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Redirecting Errors to Standard Error</title>
|
||
</head>
|
||
<body>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="writing-error-messages-to-standard-error-instead-of-standard-output"></a></p>
|
||
<h2 id="redirecting-errors-to-standard-error"><a class="header" href="#redirecting-errors-to-standard-error">Redirecting Errors to Standard Error</a></h2>
|
||
<p>At the moment, we’re writing all of our output to the terminal using the
|
||
<code>println!</code> macro. In most terminals, there are two kinds of output: <em>standard
|
||
output</em> (<code>stdout</code>) for general information and <em>standard error</em> (<code>stderr</code>) for
|
||
error messages. This distinction enables users to choose to direct the
|
||
successful output of a program to a file but still print error messages to the
|
||
screen.</p>
|
||
<p>The <code>println!</code> macro is only capable of printing to standard output, so we have
|
||
to use something else to print to standard error.</p>
|
||
<h3 id="checking-where-errors-are-written"><a class="header" href="#checking-where-errors-are-written">Checking Where Errors Are Written</a></h3>
|
||
<p>First, let’s observe how the content printed by <code>minigrep</code> is currently being
|
||
written to standard output, including any error messages we want to write to
|
||
standard error instead. We’ll do that by redirecting the standard output stream
|
||
to a file while intentionally causing an error. We won’t redirect the standard
|
||
error stream, so any content sent to standard error will continue to display on
|
||
the screen.</p>
|
||
<p>Command line programs are expected to send error messages to the standard error
|
||
stream so that we can still see error messages on the screen even if we
|
||
redirect the standard output stream to a file. Our program is not currently
|
||
well behaved: We’re about to see that it saves the error message output to a
|
||
file instead!</p>
|
||
<p>To demonstrate this behavior, we’ll run the program with <code>></code> and the file path,
|
||
<em>output.txt</em>, that we want to redirect the standard output stream to. We won’t
|
||
pass any arguments, which should cause an error:</p>
|
||
<pre><code class="language-console">$ cargo run > output.txt
|
||
</code></pre>
|
||
<p>The <code>></code> syntax tells the shell to write the contents of standard output to
|
||
<em>output.txt</em> instead of the screen. We didn’t see the error message we were
|
||
expecting printed to the screen, so that means it must have ended up in the
|
||
file. This is what <em>output.txt</em> contains:</p>
|
||
<pre><code class="language-text">Problem parsing arguments: not enough arguments
|
||
</code></pre>
|
||
<p>Yup, our error message is being printed to standard output. It’s much more
|
||
useful for error messages like this to be printed to standard error so that
|
||
only data from a successful run ends up in the file. We’ll change that.</p>
|
||
<h3 id="printing-errors-to-standard-error"><a class="header" href="#printing-errors-to-standard-error">Printing Errors to Standard Error</a></h3>
|
||
<p>We’ll use the code in Listing 12-24 to change how error messages are printed.
|
||
Because of the refactoring we did earlier in this chapter, all the code that
|
||
prints error messages is in one function, <code>main</code>. The standard library provides
|
||
the <code>eprintln!</code> macro that prints to the standard error stream, so let’s change
|
||
the two places we were calling <code>println!</code> to print errors to use <code>eprintln!</code>
|
||
instead.</p>
|
||
<figure class="listing" id="listing-12-24">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre><code class="language-rust ignore"><span class="boring">use std::env;
|
||
</span><span class="boring">use std::error::Error;
|
||
</span><span class="boring">use std::fs;
|
||
</span><span class="boring">use std::process;
|
||
</span><span class="boring">
|
||
</span><span class="boring">use minigrep::{search, search_case_insensitive};
|
||
</span><span class="boring">
|
||
</span>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) = run(config) {
|
||
eprintln!("Application error: {e}");
|
||
process::exit(1);
|
||
}
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring">pub struct Config {
|
||
</span><span class="boring"> pub query: String,
|
||
</span><span class="boring"> pub file_path: String,
|
||
</span><span class="boring"> pub ignore_case: bool,
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">impl Config {
|
||
</span><span class="boring"> fn build(args: &[String]) -> Result<Config, &'static str> {
|
||
</span><span class="boring"> if args.len() < 3 {
|
||
</span><span class="boring"> return Err("not enough arguments");
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">
|
||
</span><span class="boring"> let query = args[1].clone();
|
||
</span><span class="boring"> let file_path = args[2].clone();
|
||
</span><span class="boring">
|
||
</span><span class="boring"> let ignore_case = env::var("IGNORE_CASE").is_ok();
|
||
</span><span class="boring">
|
||
</span><span class="boring"> Ok(Config {
|
||
</span><span class="boring"> query,
|
||
</span><span class="boring"> file_path,
|
||
</span><span class="boring"> ignore_case,
|
||
</span><span class="boring"> })
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">}
|
||
</span><span class="boring">
|
||
</span><span class="boring">fn run(config: Config) -> Result<(), Box<dyn Error>> {
|
||
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
|
||
</span><span class="boring">
|
||
</span><span class="boring"> let results = if config.ignore_case {
|
||
</span><span class="boring"> search_case_insensitive(&config.query, &contents)
|
||
</span><span class="boring"> } else {
|
||
</span><span class="boring"> search(&config.query, &contents)
|
||
</span><span class="boring"> };
|
||
</span><span class="boring">
|
||
</span><span class="boring"> for line in results {
|
||
</span><span class="boring"> println!("{line}");
|
||
</span><span class="boring"> }
|
||
</span><span class="boring">
|
||
</span><span class="boring"> Ok(())
|
||
</span><span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-12-24">Listing 12-24</a>: Writing error messages to standard error instead of standard output using <code>eprintln!</code></figcaption>
|
||
</figure>
|
||
<p>Let’s now run the program again in the same way, without any arguments and
|
||
redirecting standard output with <code>></code>:</p>
|
||
<pre><code class="language-console">$ cargo run > output.txt
|
||
Problem parsing arguments: not enough arguments
|
||
</code></pre>
|
||
<p>Now we see the error onscreen and <em>output.txt</em> contains nothing, which is the
|
||
behavior we expect of command line programs.</p>
|
||
<p>Let’s run the program again with arguments that don’t cause an error but still
|
||
redirect standard output to a file, like so:</p>
|
||
<pre><code class="language-console">$ cargo run -- to poem.txt > output.txt
|
||
</code></pre>
|
||
<p>We won’t see any output to the terminal, and <em>output.txt</em> will contain our
|
||
results:</p>
|
||
<p><span class="filename">Filename: output.txt</span></p>
|
||
<pre><code class="language-text">Are you nobody, too?
|
||
How dreary to be somebody!
|
||
</code></pre>
|
||
<p>This demonstrates that we’re now using standard output for successful output
|
||
and standard error for error output as appropriate.</p>
|
||
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
|
||
<p>This chapter recapped some of the major concepts you’ve learned so far and
|
||
covered how to perform common I/O operations in Rust. By using command line
|
||
arguments, files, environment variables, and the <code>eprintln!</code> macro for printing
|
||
errors, you’re now prepared to write command line applications. Combined with
|
||
the concepts in previous chapters, your code will be well organized, store data
|
||
effectively in the appropriate data structures, handle errors nicely, and be
|
||
well tested.</p>
|
||
<p>Next, we’ll explore some Rust features that were influenced by functional
|
||
languages: closures and iterators.</p>
|
||
</body>
|
||
</html>
|