569 lines
36 KiB
HTML
569 lines
36 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Recoverable Errors with Result</title>
|
||
</head>
|
||
<body>
|
||
<h2 id="recoverable-errors-with-result"><a class="header" href="#recoverable-errors-with-result">Recoverable Errors with <code>Result</code></a></h2>
|
||
<p>Most errors aren’t serious enough to require the program to stop entirely.
|
||
Sometimes when a function fails, it’s for a reason that you can easily interpret
|
||
and respond to. For example, if you try to open a file and that operation fails
|
||
because the file doesn’t exist, you might want to create the file instead of
|
||
terminating the process.</p>
|
||
<p>Recall from <a href="../ch02/ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-result">“Handling Potential Failure with <code>Result</code>”</a><!--
|
||
ignore --> in Chapter 2 that the <code>Result</code> enum is defined as having two
|
||
variants, <code>Ok</code> and <code>Err</code>, as follows:</p>
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span>enum Result<T, E> {
|
||
Ok(T),
|
||
Err(E),
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<p>The <code>T</code> and <code>E</code> are generic type parameters: We’ll discuss generics in more
|
||
detail in Chapter 10. What you need to know right now is that <code>T</code> represents
|
||
the type of the value that will be returned in a success case within the <code>Ok</code>
|
||
variant, and <code>E</code> represents the type of the error that will be returned in a
|
||
failure case within the <code>Err</code> variant. Because <code>Result</code> has these generic type
|
||
parameters, we can use the <code>Result</code> type and the functions defined on it in
|
||
many different situations where the success value and error value we want to
|
||
return may differ.</p>
|
||
<p>Let’s call a function that returns a <code>Result</code> value because the function could
|
||
fail. In Listing 9-3, we try to open a file.</p>
|
||
<figure class="listing" id="listing-9-3">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">use std::fs::File;
|
||
|
||
fn main() {
|
||
let greeting_file_result = File::open("hello.txt");
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-9-3">Listing 9-3</a>: Opening a file</figcaption>
|
||
</figure>
|
||
<p>The return type of <code>File::open</code> is a <code>Result<T, E></code>. The generic parameter <code>T</code>
|
||
has been filled in by the implementation of <code>File::open</code> with the type of the
|
||
success value, <code>std::fs::File</code>, which is a file handle. The type of <code>E</code> used in
|
||
the error value is <code>std::io::Error</code>. This return type means the call to
|
||
<code>File::open</code> might succeed and return a file handle that we can read from or
|
||
write to. The function call also might fail: For example, the file might not
|
||
exist, or we might not have permission to access the file. The <code>File::open</code>
|
||
function needs to have a way to tell us whether it succeeded or failed and at
|
||
the same time give us either the file handle or error information. This
|
||
information is exactly what the <code>Result</code> enum conveys.</p>
|
||
<p>In the case where <code>File::open</code> succeeds, the value in the variable
|
||
<code>greeting_file_result</code> will be an instance of <code>Ok</code> that contains a file handle.
|
||
In the case where it fails, the value in <code>greeting_file_result</code> will be an
|
||
instance of <code>Err</code> that contains more information about the kind of error that
|
||
occurred.</p>
|
||
<p>We need to add to the code in Listing 9-3 to take different actions depending
|
||
on the value <code>File::open</code> returns. Listing 9-4 shows one way to handle the
|
||
<code>Result</code> using a basic tool, the <code>match</code> expression that we discussed in
|
||
Chapter 6.</p>
|
||
<figure class="listing" id="listing-9-4">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust should_panic edition2024">use std::fs::File;
|
||
|
||
fn main() {
|
||
let greeting_file_result = File::open("hello.txt");
|
||
|
||
let greeting_file = match greeting_file_result {
|
||
Ok(file) => file,
|
||
Err(error) => panic!("Problem opening the file: {error:?}"),
|
||
};
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-9-4">Listing 9-4</a>: Using a <code>match</code> expression to handle the <code>Result</code> variants that might be returned</figcaption>
|
||
</figure>
|
||
<p>Note that, like the <code>Option</code> enum, the <code>Result</code> enum and its variants have been
|
||
brought into scope by the prelude, so we don’t need to specify <code>Result::</code>
|
||
before the <code>Ok</code> and <code>Err</code> variants in the <code>match</code> arms.</p>
|
||
<p>When the result is <code>Ok</code>, this code will return the inner <code>file</code> value out of
|
||
the <code>Ok</code> variant, and we then assign that file handle value to the variable
|
||
<code>greeting_file</code>. After the <code>match</code>, we can use the file handle for reading or
|
||
writing.</p>
|
||
<p>The other arm of the <code>match</code> handles the case where we get an <code>Err</code> value from
|
||
<code>File::open</code>. In this example, we’ve chosen to call the <code>panic!</code> macro. If
|
||
there’s no file named <em>hello.txt</em> in our current directory and we run this
|
||
code, we’ll see the following output from the <code>panic!</code> macro:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling error-handling v0.1.0 (file:///projects/error-handling)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.73s
|
||
Running `target/debug/error-handling`
|
||
|
||
thread 'main' panicked at src/main.rs:8:23:
|
||
Problem opening the file: Os { code: 2, kind: NotFound, message: "No such file or directory" }
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
</code></pre>
|
||
<p>As usual, this output tells us exactly what has gone wrong.</p>
|
||
<h3 id="matching-on-different-errors"><a class="header" href="#matching-on-different-errors">Matching on Different Errors</a></h3>
|
||
<p>The code in Listing 9-4 will <code>panic!</code> no matter why <code>File::open</code> failed.
|
||
However, we want to take different actions for different failure reasons. If
|
||
<code>File::open</code> failed because the file doesn’t exist, we want to create the file
|
||
and return the handle to the new file. If <code>File::open</code> failed for any other
|
||
reason—for example, because we didn’t have permission to open the file—we still
|
||
want the code to <code>panic!</code> in the same way it did in Listing 9-4. For this, we
|
||
add an inner <code>match</code> expression, shown in Listing 9-5.</p>
|
||
<figure class="listing" id="listing-9-5">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<!-- ignore this test because otherwise it creates hello.txt which causes other
|
||
tests to fail lol -->
|
||
<pre><code class="language-rust ignore">use std::fs::File;
|
||
use std::io::ErrorKind;
|
||
|
||
fn main() {
|
||
let greeting_file_result = File::open("hello.txt");
|
||
|
||
let greeting_file = match greeting_file_result {
|
||
Ok(file) => file,
|
||
Err(error) => match error.kind() {
|
||
ErrorKind::NotFound => match File::create("hello.txt") {
|
||
Ok(fc) => fc,
|
||
Err(e) => panic!("Problem creating the file: {e:?}"),
|
||
},
|
||
_ => {
|
||
panic!("Problem opening the file: {error:?}");
|
||
}
|
||
},
|
||
};
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-9-5">Listing 9-5</a>: Handling different kinds of errors in different ways</figcaption>
|
||
</figure>
|
||
<p>The type of the value that <code>File::open</code> returns inside the <code>Err</code> variant is
|
||
<code>io::Error</code>, which is a struct provided by the standard library. This struct
|
||
has a method, <code>kind</code>, that we can call to get an <code>io::ErrorKind</code> value. The
|
||
enum <code>io::ErrorKind</code> is provided by the standard library and has variants
|
||
representing the different kinds of errors that might result from an <code>io</code>
|
||
operation. The variant we want to use is <code>ErrorKind::NotFound</code>, which indicates
|
||
the file we’re trying to open doesn’t exist yet. So, we match on
|
||
<code>greeting_file_result</code>, but we also have an inner match on <code>error.kind()</code>.</p>
|
||
<p>The condition we want to check in the inner match is whether the value returned
|
||
by <code>error.kind()</code> is the <code>NotFound</code> variant of the <code>ErrorKind</code> enum. If it is,
|
||
we try to create the file with <code>File::create</code>. However, because <code>File::create</code>
|
||
could also fail, we need a second arm in the inner <code>match</code> expression. When the
|
||
file can’t be created, a different error message is printed. The second arm of
|
||
the outer <code>match</code> stays the same, so the program panics on any error besides
|
||
the missing file error.</p>
|
||
<section class="note" aria-role="note">
|
||
<h4 id="alternatives-to-using-match-with-resultt-e"><a class="header" href="#alternatives-to-using-match-with-resultt-e">Alternatives to Using <code>match</code> with <code>Result<T, E></code></a></h4>
|
||
<p>That’s a lot of <code>match</code>! The <code>match</code> expression is very useful but also very
|
||
much a primitive. In Chapter 13, you’ll learn about closures, which are used
|
||
with many of the methods defined on <code>Result<T, E></code>. These methods can be more
|
||
concise than using <code>match</code> when handling <code>Result<T, E></code> values in your code.</p>
|
||
<p>For example, here’s another way to write the same logic as shown in Listing
|
||
9-5, this time using closures and the <code>unwrap_or_else</code> method:</p>
|
||
<!-- CAN'T EXTRACT SEE https://github.com/rust-lang/mdBook/issues/1127 -->
|
||
<pre><code class="language-rust ignore">use std::fs::File;
|
||
use std::io::ErrorKind;
|
||
|
||
fn main() {
|
||
let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
|
||
if error.kind() == ErrorKind::NotFound {
|
||
File::create("hello.txt").unwrap_or_else(|error| {
|
||
panic!("Problem creating the file: {error:?}");
|
||
})
|
||
} else {
|
||
panic!("Problem opening the file: {error:?}");
|
||
}
|
||
});
|
||
}</code></pre>
|
||
<p>Although this code has the same behavior as Listing 9-5, it doesn’t contain
|
||
any <code>match</code> expressions and is cleaner to read. Come back to this example
|
||
after you’ve read Chapter 13 and look up the <code>unwrap_or_else</code> method in the
|
||
standard library documentation. Many more of these methods can clean up huge,
|
||
nested <code>match</code> expressions when you’re dealing with errors.</p>
|
||
</section>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="shortcuts-for-panic-on-error-unwrap-and-expect"></a></p>
|
||
<h4 id="shortcuts-for-panic-on-error"><a class="header" href="#shortcuts-for-panic-on-error">Shortcuts for Panic on Error</a></h4>
|
||
<p>Using <code>match</code> works well enough, but it can be a bit verbose and doesn’t always
|
||
communicate intent well. The <code>Result<T, E></code> type has many helper methods
|
||
defined on it to do various, more specific tasks. The <code>unwrap</code> method is a
|
||
shortcut method implemented just like the <code>match</code> expression we wrote in
|
||
Listing 9-4. If the <code>Result</code> value is the <code>Ok</code> variant, <code>unwrap</code> will return
|
||
the value inside the <code>Ok</code>. If the <code>Result</code> is the <code>Err</code> variant, <code>unwrap</code> will
|
||
call the <code>panic!</code> macro for us. Here is an example of <code>unwrap</code> in action:</p>
|
||
<figure class="listing">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust should_panic edition2024">use std::fs::File;
|
||
|
||
fn main() {
|
||
let greeting_file = File::open("hello.txt").unwrap();
|
||
}</code></pre>
|
||
</figure>
|
||
<p>If we run this code without a <em>hello.txt</em> file, we’ll see an error message from
|
||
the <code>panic!</code> call that the <code>unwrap</code> method makes:</p>
|
||
<!-- manual-regeneration
|
||
cd listings/ch09-error-handling/no-listing-04-unwrap
|
||
cargo run
|
||
copy and paste relevant text
|
||
-->
|
||
<pre><code class="language-text">thread 'main' panicked at src/main.rs:4:49:
|
||
called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }
|
||
</code></pre>
|
||
<p>Similarly, the <code>expect</code> method lets us also choose the <code>panic!</code> error message.
|
||
Using <code>expect</code> instead of <code>unwrap</code> and providing good error messages can convey
|
||
your intent and make tracking down the source of a panic easier. The syntax of
|
||
<code>expect</code> looks like this:</p>
|
||
<figure class="listing">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust should_panic edition2024">use std::fs::File;
|
||
|
||
fn main() {
|
||
let greeting_file = File::open("hello.txt")
|
||
.expect("hello.txt should be included in this project");
|
||
}</code></pre>
|
||
</figure>
|
||
<p>We use <code>expect</code> in the same way as <code>unwrap</code>: to return the file handle or call
|
||
the <code>panic!</code> macro. The error message used by <code>expect</code> in its call to <code>panic!</code>
|
||
will be the parameter that we pass to <code>expect</code>, rather than the default
|
||
<code>panic!</code> message that <code>unwrap</code> uses. Here’s what it looks like:</p>
|
||
<!-- manual-regeneration
|
||
cd listings/ch09-error-handling/no-listing-05-expect
|
||
cargo run
|
||
copy and paste relevant text
|
||
-->
|
||
<pre><code class="language-text">thread 'main' panicked at src/main.rs:5:10:
|
||
hello.txt should be included in this project: Os { code: 2, kind: NotFound, message: "No such file or directory" }
|
||
</code></pre>
|
||
<p>In production-quality code, most Rustaceans choose <code>expect</code> rather than
|
||
<code>unwrap</code> and give more context about why the operation is expected to always
|
||
succeed. That way, if your assumptions are ever proven wrong, you have more
|
||
information to use in debugging.</p>
|
||
<h3 id="propagating-errors"><a class="header" href="#propagating-errors">Propagating Errors</a></h3>
|
||
<p>When a function’s implementation calls something that might fail, instead of
|
||
handling the error within the function itself, you can return the error to the
|
||
calling code so that it can decide what to do. This is known as <em>propagating</em>
|
||
the error and gives more control to the calling code, where there might be more
|
||
information or logic that dictates how the error should be handled than what
|
||
you have available in the context of your code.</p>
|
||
<p>For example, Listing 9-6 shows a function that reads a username from a file. If
|
||
the file doesn’t exist or can’t be read, this function will return those errors
|
||
to the code that called the function.</p>
|
||
<figure class="listing" id="listing-9-6">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<!-- Deliberately not using rustdoc_include here; the `main` function in the
|
||
file panics. We do want to include it for reader experimentation purposes, but
|
||
don't want to include it for rustdoc testing purposes. -->
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span>use std::fs::File;
|
||
use std::io::{self, Read};
|
||
|
||
fn read_username_from_file() -> Result<String, io::Error> {
|
||
let username_file_result = File::open("hello.txt");
|
||
|
||
let mut username_file = match username_file_result {
|
||
Ok(file) => file,
|
||
Err(e) => return Err(e),
|
||
};
|
||
|
||
let mut username = String::new();
|
||
|
||
match username_file.read_to_string(&mut username) {
|
||
Ok(_) => Ok(username),
|
||
Err(e) => Err(e),
|
||
}
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-9-6">Listing 9-6</a>: A function that returns errors to the calling code using <code>match</code></figcaption>
|
||
</figure>
|
||
<p>This function can be written in a much shorter way, but we’re going to start by
|
||
doing a lot of it manually in order to explore error handling; at the end,
|
||
we’ll show the shorter way. Let’s look at the return type of the function
|
||
first: <code>Result<String, io::Error></code>. This means the function is returning a
|
||
value of the type <code>Result<T, E></code>, where the generic parameter <code>T</code> has been
|
||
filled in with the concrete type <code>String</code> and the generic type <code>E</code> has been
|
||
filled in with the concrete type <code>io::Error</code>.</p>
|
||
<p>If this function succeeds without any problems, the code that calls this
|
||
function will receive an <code>Ok</code> value that holds a <code>String</code>—the <code>username</code> that
|
||
this function read from the file. If this function encounters any problems, the
|
||
calling code will receive an <code>Err</code> value that holds an instance of <code>io::Error</code>
|
||
that contains more information about what the problems were. We chose
|
||
<code>io::Error</code> as the return type of this function because that happens to be the
|
||
type of the error value returned from both of the operations we’re calling in
|
||
this function’s body that might fail: the <code>File::open</code> function and the
|
||
<code>read_to_string</code> method.</p>
|
||
<p>The body of the function starts by calling the <code>File::open</code> function. Then, we
|
||
handle the <code>Result</code> value with a <code>match</code> similar to the <code>match</code> in Listing 9-4.
|
||
If <code>File::open</code> succeeds, the file handle in the pattern variable <code>file</code>
|
||
becomes the value in the mutable variable <code>username_file</code> and the function
|
||
continues. In the <code>Err</code> case, instead of calling <code>panic!</code>, we use the <code>return</code>
|
||
keyword to return early out of the function entirely and pass the error value
|
||
from <code>File::open</code>, now in the pattern variable <code>e</code>, back to the calling code as
|
||
this function’s error value.</p>
|
||
<p>So, if we have a file handle in <code>username_file</code>, the function then creates a
|
||
new <code>String</code> in variable <code>username</code> and calls the <code>read_to_string</code> method on
|
||
the file handle in <code>username_file</code> to read the contents of the file into
|
||
<code>username</code>. The <code>read_to_string</code> method also returns a <code>Result</code> because it
|
||
might fail, even though <code>File::open</code> succeeded. So, we need another <code>match</code> to
|
||
handle that <code>Result</code>: If <code>read_to_string</code> succeeds, then our function has
|
||
succeeded, and we return the username from the file that’s now in <code>username</code>
|
||
wrapped in an <code>Ok</code>. If <code>read_to_string</code> fails, we return the error value in the
|
||
same way that we returned the error value in the <code>match</code> that handled the
|
||
return value of <code>File::open</code>. However, we don’t need to explicitly say
|
||
<code>return</code>, because this is the last expression in the function.</p>
|
||
<p>The code that calls this code will then handle getting either an <code>Ok</code> value
|
||
that contains a username or an <code>Err</code> value that contains an <code>io::Error</code>. It’s
|
||
up to the calling code to decide what to do with those values. If the calling
|
||
code gets an <code>Err</code> value, it could call <code>panic!</code> and crash the program, use a
|
||
default username, or look up the username from somewhere other than a file, for
|
||
example. We don’t have enough information on what the calling code is actually
|
||
trying to do, so we propagate all the success or error information upward for
|
||
it to handle appropriately.</p>
|
||
<p>This pattern of propagating errors is so common in Rust that Rust provides the
|
||
question mark operator <code>?</code> to make this easier.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="a-shortcut-for-propagating-errors-the--operator"></a></p>
|
||
<h4 id="the--operator-shortcut"><a class="header" href="#the--operator-shortcut">The <code>?</code> Operator Shortcut</a></h4>
|
||
<p>Listing 9-7 shows an implementation of <code>read_username_from_file</code> that has the
|
||
same functionality as in Listing 9-6, but this implementation uses the <code>?</code>
|
||
operator.</p>
|
||
<figure class="listing" id="listing-9-7">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<!-- Deliberately not using rustdoc_include here; the `main` function in the
|
||
file panics. We do want to include it for reader experimentation purposes, but
|
||
don't want to include it for rustdoc testing purposes. -->
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span>use std::fs::File;
|
||
use std::io::{self, Read};
|
||
|
||
fn read_username_from_file() -> Result<String, io::Error> {
|
||
let mut username_file = File::open("hello.txt")?;
|
||
let mut username = String::new();
|
||
username_file.read_to_string(&mut username)?;
|
||
Ok(username)
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-9-7">Listing 9-7</a>: A function that returns errors to the calling code using the <code>?</code> operator</figcaption>
|
||
</figure>
|
||
<p>The <code>?</code> placed after a <code>Result</code> value is defined to work in almost the same way
|
||
as the <code>match</code> expressions that we defined to handle the <code>Result</code> values in
|
||
Listing 9-6. If the value of the <code>Result</code> is an <code>Ok</code>, the value inside the <code>Ok</code>
|
||
will get returned from this expression, and the program will continue. If the
|
||
value is an <code>Err</code>, the <code>Err</code> will be returned from the whole function as if we
|
||
had used the <code>return</code> keyword so that the error value gets propagated to the
|
||
calling code.</p>
|
||
<p>There is a difference between what the <code>match</code> expression from Listing 9-6 does
|
||
and what the <code>?</code> operator does: Error values that have the <code>?</code> operator called
|
||
on them go through the <code>from</code> function, defined in the <code>From</code> trait in the
|
||
standard library, which is used to convert values from one type into another.
|
||
When the <code>?</code> operator calls the <code>from</code> function, the error type received is
|
||
converted into the error type defined in the return type of the current
|
||
function. This is useful when a function returns one error type to represent
|
||
all the ways a function might fail, even if parts might fail for many different
|
||
reasons.</p>
|
||
<p>For example, we could change the <code>read_username_from_file</code> function in Listing
|
||
9-7 to return a custom error type named <code>OurError</code> that we define. If we also
|
||
define <code>impl From<io::Error> for OurError</code> to construct an instance of
|
||
<code>OurError</code> from an <code>io::Error</code>, then the <code>?</code> operator calls in the body of
|
||
<code>read_username_from_file</code> will call <code>from</code> and convert the error types without
|
||
needing to add any more code to the function.</p>
|
||
<p>In the context of Listing 9-7, the <code>?</code> at the end of the <code>File::open</code> call will
|
||
return the value inside an <code>Ok</code> to the variable <code>username_file</code>. If an error
|
||
occurs, the <code>?</code> operator will return early out of the whole function and give
|
||
any <code>Err</code> value to the calling code. The same thing applies to the <code>?</code> at the
|
||
end of the <code>read_to_string</code> call.</p>
|
||
<p>The <code>?</code> operator eliminates a lot of boilerplate and makes this function’s
|
||
implementation simpler. We could even shorten this code further by chaining
|
||
method calls immediately after the <code>?</code>, as shown in Listing 9-8.</p>
|
||
<figure class="listing" id="listing-9-8">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<!-- Deliberately not using rustdoc_include here; the `main` function in the
|
||
file panics. We do want to include it for reader experimentation purposes, but
|
||
don't want to include it for rustdoc testing purposes. -->
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span>use std::fs::File;
|
||
use std::io::{self, Read};
|
||
|
||
fn read_username_from_file() -> Result<String, io::Error> {
|
||
let mut username = String::new();
|
||
|
||
File::open("hello.txt")?.read_to_string(&mut username)?;
|
||
|
||
Ok(username)
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-9-8">Listing 9-8</a>: Chaining method calls after the <code>?</code> operator</figcaption>
|
||
</figure>
|
||
<p>We’ve moved the creation of the new <code>String</code> in <code>username</code> to the beginning of
|
||
the function; that part hasn’t changed. Instead of creating a variable
|
||
<code>username_file</code>, we’ve chained the call to <code>read_to_string</code> directly onto the
|
||
result of <code>File::open("hello.txt")?</code>. We still have a <code>?</code> at the end of the
|
||
<code>read_to_string</code> call, and we still return an <code>Ok</code> value containing <code>username</code>
|
||
when both <code>File::open</code> and <code>read_to_string</code> succeed rather than returning
|
||
errors. The functionality is again the same as in Listing 9-6 and Listing 9-7;
|
||
this is just a different, more ergonomic way to write it.</p>
|
||
<p>Listing 9-9 shows a way to make this even shorter using <code>fs::read_to_string</code>.</p>
|
||
<figure class="listing" id="listing-9-9">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<!-- Deliberately not using rustdoc_include here; the `main` function in the
|
||
file panics. We do want to include it for reader experimentation purposes, but
|
||
don't want to include it for rustdoc testing purposes. -->
|
||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||
</span><span class="boring">fn main() {
|
||
</span>use std::fs;
|
||
use std::io;
|
||
|
||
fn read_username_from_file() -> Result<String, io::Error> {
|
||
fs::read_to_string("hello.txt")
|
||
}
|
||
<span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-9-9">Listing 9-9</a>: Using <code>fs::read_to_string</code> instead of opening and then reading the file</figcaption>
|
||
</figure>
|
||
<p>Reading a file into a string is a fairly common operation, so the standard
|
||
library provides the convenient <code>fs::read_to_string</code> function that opens the
|
||
file, creates a new <code>String</code>, reads the contents of the file, puts the contents
|
||
into that <code>String</code>, and returns it. Of course, using <code>fs::read_to_string</code>
|
||
doesn’t give us the opportunity to explain all the error handling, so we did it
|
||
the longer way first.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="where-the--operator-can-be-used"></a></p>
|
||
<h4 id="where-to-use-the--operator"><a class="header" href="#where-to-use-the--operator">Where to Use the <code>?</code> Operator</a></h4>
|
||
<p>The <code>?</code> operator can only be used in functions whose return type is compatible
|
||
with the value the <code>?</code> is used on. This is because the <code>?</code> operator is defined
|
||
to perform an early return of a value out of the function, in the same manner
|
||
as the <code>match</code> expression we defined in Listing 9-6. In Listing 9-6, the
|
||
<code>match</code> was using a <code>Result</code> value, and the early return arm returned an
|
||
<code>Err(e)</code> value. The return type of the function has to be a <code>Result</code> so that
|
||
it’s compatible with this <code>return</code>.</p>
|
||
<p>In Listing 9-10, let’s look at the error we’ll get if we use the <code>?</code> operator
|
||
in a <code>main</code> function with a return type that is incompatible with the type of
|
||
the value we use <code>?</code> on.</p>
|
||
<figure class="listing" id="listing-9-10">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre><code class="language-rust ignore does_not_compile">use std::fs::File;
|
||
|
||
fn main() {
|
||
let greeting_file = File::open("hello.txt")?;
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-9-10">Listing 9-10</a>: Attempting to use the <code>?</code> in the <code>main</code> function that returns <code>()</code> won’t compile.</figcaption>
|
||
</figure>
|
||
<p>This code opens a file, which might fail. The <code>?</code> operator follows the <code>Result</code>
|
||
value returned by <code>File::open</code>, but this <code>main</code> function has the return type of
|
||
<code>()</code>, not <code>Result</code>. When we compile this code, we get the following error
|
||
message:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling error-handling v0.1.0 (file:///projects/error-handling)
|
||
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
|
||
--> src/main.rs:4:48
|
||
|
|
||
3 | fn main() {
|
||
| --------- this function should return `Result` or `Option` to accept `?`
|
||
4 | let greeting_file = File::open("hello.txt")?;
|
||
| ^ cannot use the `?` operator in a function that returns `()`
|
||
|
|
||
help: consider adding return type
|
||
|
|
||
3 ~ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||
4 | let greeting_file = File::open("hello.txt")?;
|
||
5 + Ok(())
|
||
|
|
||
|
||
For more information about this error, try `rustc --explain E0277`.
|
||
error: could not compile `error-handling` (bin "error-handling") due to 1 previous error
|
||
</code></pre>
|
||
<p>This error points out that we’re only allowed to use the <code>?</code> operator in a
|
||
function that returns <code>Result</code>, <code>Option</code>, or another type that implements
|
||
<code>FromResidual</code>.</p>
|
||
<p>To fix the error, you have two choices. One choice is to change the return type
|
||
of your function to be compatible with the value you’re using the <code>?</code> operator
|
||
on as long as you have no restrictions preventing that. The other choice is to
|
||
use a <code>match</code> or one of the <code>Result<T, E></code> methods to handle the <code>Result<T, E></code>
|
||
in whatever way is appropriate.</p>
|
||
<p>The error message also mentioned that <code>?</code> can be used with <code>Option<T></code> values
|
||
as well. As with using <code>?</code> on <code>Result</code>, you can only use <code>?</code> on <code>Option</code> in a
|
||
function that returns an <code>Option</code>. The behavior of the <code>?</code> operator when called
|
||
on an <code>Option<T></code> is similar to its behavior when called on a <code>Result<T, E></code>:
|
||
If the value is <code>None</code>, the <code>None</code> will be returned early from the function at
|
||
that point. If the value is <code>Some</code>, the value inside the <code>Some</code> is the
|
||
resultant value of the expression, and the function continues. Listing 9-11 has
|
||
an example of a function that finds the last character of the first line in the
|
||
given text.</p>
|
||
<figure class="listing" id="listing-9-11">
|
||
<pre class="playground"><code class="language-rust edition2024">fn last_char_of_first_line(text: &str) -> Option<char> {
|
||
text.lines().next()?.chars().last()
|
||
}
|
||
<span class="boring">
|
||
</span><span class="boring">fn main() {
|
||
</span><span class="boring"> assert_eq!(
|
||
</span><span class="boring"> last_char_of_first_line("Hello, world\nHow are you today?"),
|
||
</span><span class="boring"> Some('d')
|
||
</span><span class="boring"> );
|
||
</span><span class="boring">
|
||
</span><span class="boring"> assert_eq!(last_char_of_first_line(""), None);
|
||
</span><span class="boring"> assert_eq!(last_char_of_first_line("\nhi"), None);
|
||
</span><span class="boring">}</span></code></pre>
|
||
<figcaption><a href="#listing-9-11">Listing 9-11</a>: Using the <code>?</code> operator on an <code>Option<T></code> value</figcaption>
|
||
</figure>
|
||
<p>This function returns <code>Option<char></code> because it’s possible that there is a
|
||
character there, but it’s also possible that there isn’t. This code takes the
|
||
<code>text</code> string slice argument and calls the <code>lines</code> method on it, which returns
|
||
an iterator over the lines in the string. Because this function wants to
|
||
examine the first line, it calls <code>next</code> on the iterator to get the first value
|
||
from the iterator. If <code>text</code> is the empty string, this call to <code>next</code> will
|
||
return <code>None</code>, in which case we use <code>?</code> to stop and return <code>None</code> from
|
||
<code>last_char_of_first_line</code>. If <code>text</code> is not the empty string, <code>next</code> will
|
||
return a <code>Some</code> value containing a string slice of the first line in <code>text</code>.</p>
|
||
<p>The <code>?</code> extracts the string slice, and we can call <code>chars</code> on that string slice
|
||
to get an iterator of its characters. We’re interested in the last character in
|
||
this first line, so we call <code>last</code> to return the last item in the iterator.
|
||
This is an <code>Option</code> because it’s possible that the first line is the empty
|
||
string; for example, if <code>text</code> starts with a blank line but has characters on
|
||
other lines, as in <code>"\nhi"</code>. However, if there is a last character on the first
|
||
line, it will be returned in the <code>Some</code> variant. The <code>?</code> operator in the middle
|
||
gives us a concise way to express this logic, allowing us to implement the
|
||
function in one line. If we couldn’t use the <code>?</code> operator on <code>Option</code>, we’d
|
||
have to implement this logic using more method calls or a <code>match</code> expression.</p>
|
||
<p>Note that you can use the <code>?</code> operator on a <code>Result</code> in a function that returns
|
||
<code>Result</code>, and you can use the <code>?</code> operator on an <code>Option</code> in a function that
|
||
returns <code>Option</code>, but you can’t mix and match. The <code>?</code> operator won’t
|
||
automatically convert a <code>Result</code> to an <code>Option</code> or vice versa; in those cases,
|
||
you can use methods like the <code>ok</code> method on <code>Result</code> or the <code>ok_or</code> method on
|
||
<code>Option</code> to do the conversion explicitly.</p>
|
||
<p>So far, all the <code>main</code> functions we’ve used return <code>()</code>. The <code>main</code> function is
|
||
special because it’s the entry point and exit point of an executable program,
|
||
and there are restrictions on what its return type can be for the program to
|
||
behave as expected.</p>
|
||
<p>Luckily, <code>main</code> can also return a <code>Result<(), E></code>. Listing 9-12 has the code
|
||
from Listing 9-10, but we’ve changed the return type of <code>main</code> to be
|
||
<code>Result<(), Box<dyn Error>></code> and added a return value <code>Ok(())</code> to the end. This
|
||
code will now compile.</p>
|
||
<figure class="listing" id="listing-9-12">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre><code class="language-rust ignore">use std::error::Error;
|
||
use std::fs::File;
|
||
|
||
fn main() -> Result<(), Box<dyn Error>> {
|
||
let greeting_file = File::open("hello.txt")?;
|
||
|
||
Ok(())
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-9-12">Listing 9-12</a>: Changing <code>main</code> to return <code>Result<(), E></code> allows the use of the <code>?</code> operator on <code>Result</code> values.</figcaption>
|
||
</figure>
|
||
<p>The <code>Box<dyn Error></code> type is a trait object, which we’ll talk about in <a href="../ch18/ch18-02-trait-objects.html#using-trait-objects-to-abstract-over-shared-behavior">“Using
|
||
Trait Objects to Abstract over Shared Behavior”</a><!-- ignore -->
|
||
in Chapter 18. For now, you can read <code>Box<dyn Error></code> to mean “any kind of
|
||
error.” Using <code>?</code> on a <code>Result</code> value in a <code>main</code> function with the error type
|
||
<code>Box<dyn Error></code> is allowed because it allows any <code>Err</code> value to be returned
|
||
early. Even though the body of this <code>main</code> function will only ever return
|
||
errors of type <code>std::io::Error</code>, by specifying <code>Box<dyn Error></code>, this signature
|
||
will continue to be correct even if more code that returns other errors is
|
||
added to the body of <code>main</code>.</p>
|
||
<p>When a <code>main</code> function returns a <code>Result<(), E></code>, the executable will exit with
|
||
a value of <code>0</code> if <code>main</code> returns <code>Ok(())</code> and will exit with a nonzero value if
|
||
<code>main</code> returns an <code>Err</code> value. Executables written in C return integers when
|
||
they exit: Programs that exit successfully return the integer <code>0</code>, and programs
|
||
that error return some integer other than <code>0</code>. Rust also returns integers from
|
||
executables to be compatible with this convention.</p>
|
||
<p>The <code>main</code> function may return any types that implement <a href="../std/process/trait.Termination.html">the
|
||
<code>std::process::Termination</code> trait</a><!-- ignore -->, which contains
|
||
a function <code>report</code> that returns an <code>ExitCode</code>. Consult the standard library
|
||
documentation for more information on implementing the <code>Termination</code> trait for
|
||
your own types.</p>
|
||
<p>Now that we’ve discussed the details of calling <code>panic!</code> or returning <code>Result</code>,
|
||
let’s return to the topic of how to decide which is appropriate to use in which
|
||
cases.</p>
|
||
</body>
|
||
</html>
|