Files
docs-rust/ch12/ch12-06-writing-to-stderr-instead-of-stdout.html
2026-06-22 21:27:36 +05:30

148 lines
8.1 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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, were 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, lets 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. Well do that by redirecting the standard output stream
to a file while intentionally causing an error. We wont 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: Were about to see that it saves the error message output to a
file instead!</p>
<p>To demonstrate this behavior, well run the program with <code>&gt;</code> and the file path,
<em>output.txt</em>, that we want to redirect the standard output stream to. We wont
pass any arguments, which should cause an error:</p>
<pre><code class="language-console">$ cargo run &gt; output.txt
</code></pre>
<p>The <code>&gt;</code> syntax tells the shell to write the contents of standard output to
<em>output.txt</em> instead of the screen. We didnt 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. Its 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. Well 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>Well 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 lets 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&lt;String&gt; = env::args().collect();
let config = Config::build(&amp;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: &amp;[String]) -&gt; Result&lt;Config, &amp;'static str&gt; {
</span><span class="boring"> if args.len() &lt; 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) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
</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(&amp;config.query, &amp;contents)
</span><span class="boring"> } else {
</span><span class="boring"> search(&amp;config.query, &amp;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>Lets now run the program again in the same way, without any arguments and
redirecting standard output with <code>&gt;</code>:</p>
<pre><code class="language-console">$ cargo run &gt; 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>Lets run the program again with arguments that dont cause an error but still
redirect standard output to a file, like so:</p>
<pre><code class="language-console">$ cargo run -- to poem.txt &gt; output.txt
</code></pre>
<p>We wont 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 were 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 youve 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, youre 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, well explore some Rust features that were influenced by functional
languages: closures and iterators.</p>
</body>
</html>