674 lines
53 KiB
HTML
674 lines
53 KiB
HTML
|
<!DOCTYPE HTML>
|
|||
|
<html lang="en" class="sidebar-visible no-js light">
|
|||
|
<head>
|
|||
|
<!-- Book generated using mdBook -->
|
|||
|
<meta charset="UTF-8">
|
|||
|
<title>Recoverable Errors with Result - The Rust Programming Language</title>
|
|||
|
|
|||
|
|
|||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|||
|
<meta name="description" content="">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|||
|
<meta name="theme-color" content="#ffffff" />
|
|||
|
|
|||
|
<link rel="shortcut icon" href="favicon.png">
|
|||
|
<link rel="stylesheet" href="css/variables.css">
|
|||
|
<link rel="stylesheet" href="css/general.css">
|
|||
|
<link rel="stylesheet" href="css/chrome.css">
|
|||
|
<link rel="stylesheet" href="css/print.css" media="print">
|
|||
|
|
|||
|
<!-- Fonts -->
|
|||
|
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
|||
|
<link href="googleFonts/css.css" rel="stylesheet" type="text/css">
|
|||
|
|
|||
|
<!-- Highlight.js Stylesheets -->
|
|||
|
<link rel="stylesheet" href="highlight.css">
|
|||
|
<link rel="stylesheet" href="tomorrow-night.css">
|
|||
|
<link rel="stylesheet" href="ayu-highlight.css">
|
|||
|
|
|||
|
<!-- Custom theme stylesheets -->
|
|||
|
|
|||
|
<link rel="stylesheet" href="ferris.css">
|
|||
|
|
|||
|
<link rel="stylesheet" href="theme/2018-edition.css">
|
|||
|
|
|||
|
|
|||
|
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<!-- Provide site root to javascript -->
|
|||
|
<script type="text/javascript">
|
|||
|
var path_to_root = "";
|
|||
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|||
|
<script type="text/javascript">
|
|||
|
try {
|
|||
|
var theme = localStorage.getItem('mdbook-theme');
|
|||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|||
|
|
|||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|||
|
}
|
|||
|
|
|||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|||
|
}
|
|||
|
} catch (e) { }
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|||
|
<script type="text/javascript">
|
|||
|
var theme;
|
|||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|||
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|||
|
var html = document.querySelector('html');
|
|||
|
html.classList.remove('no-js')
|
|||
|
html.classList.remove('light')
|
|||
|
html.classList.add(theme);
|
|||
|
html.classList.add('js');
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|||
|
<script type="text/javascript">
|
|||
|
var html = document.querySelector('html');
|
|||
|
var sidebar = 'hidden';
|
|||
|
if (document.body.clientWidth >= 1080) {
|
|||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|||
|
sidebar = sidebar || 'visible';
|
|||
|
}
|
|||
|
html.classList.remove('sidebar-visible');
|
|||
|
html.classList.add("sidebar-" + sidebar);
|
|||
|
</script>
|
|||
|
|
|||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|||
|
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
|||
|
<ol class="chapter"><li class="expanded affix "><a href="title-page.html">The Rust Programming Language</a></li><li class="expanded affix "><a href="foreword.html">Foreword</a></li><li class="expanded affix "><a href="ch00-00-introduction.html">Introduction</a></li><li class="expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> Installation</a></li><li class="expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Hello, Cargo!</a></li></ol></li><li class="expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> Programming a Guessing Game</a></li><li class="expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> Common Programming Concepts</a></li><li><ol class="section"><li class="expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> Variables and Mutability</a></li><li class="expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> Data Types</a></li><li class="expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> Functions</a></li><li class="expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> Comments</a></li><li class="expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> Control Flow</a></li></ol></li><li class="expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> Understanding Ownership</a></li><li><ol class="section"><li class="expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> What is Ownership?</a></li><li class="expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> References and Borrowing</a></li><li class="expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> The Slice Type</a></li></ol></li><li class="expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> Using Structs to Structure Related Data</a></li><li><ol class="section"><li class="expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> Defining and Instantiating Structs</a></li><li class="expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> An Example Program Using Structs</a></li><li class="expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> Method Syntax</a></li></ol></li><li class="expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> Enums and Pattern Matching</a></li><li><ol class="section"><li class="expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> Defining an Enum</a></li><li class="expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> The match Control Flow Operator</a></li><li class="expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> Concise Control Flow with if let</a></li></ol></li><li class="expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> Managing Growing Projects with Packages, Crates, and Modules</a></li><li><ol class="section"><li class="expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> Packages and Crates</a></li><li class="expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> Defining Modules to Control Scope and Privacy</a></li><li class="expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><
|
|||
|
</div>
|
|||
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
|||
|
</nav>
|
|||
|
|
|||
|
<div id="page-wrapper" class="page-wrapper">
|
|||
|
|
|||
|
<div class="page">
|
|||
|
|
|||
|
<div id="menu-bar" class="menu-bar">
|
|||
|
<div id="menu-bar-sticky-container">
|
|||
|
<div class="left-buttons">
|
|||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|||
|
<i class="fa fa-bars"></i>
|
|||
|
</button>
|
|||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|||
|
<i class="fa fa-paint-brush"></i>
|
|||
|
</button>
|
|||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|||
|
<i class="fa fa-search"></i>
|
|||
|
</button>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
<h1 class="menu-title">The Rust Programming Language</h1>
|
|||
|
|
|||
|
<div class="right-buttons">
|
|||
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
|||
|
<i id="print-button" class="fa fa-print"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<div id="search-wrapper" class="hidden">
|
|||
|
<form id="searchbar-outer" class="searchbar-outer">
|
|||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|||
|
</form>
|
|||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|||
|
<ul id="searchresults">
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|||
|
<script type="text/javascript">
|
|||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|||
|
});
|
|||
|
</script>
|
|||
|
|
|||
|
<div id="content" class="content">
|
|||
|
<main>
|
|||
|
<h2><a class="header" href="#recoverable-errors-with-result" id="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-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type">“Handling Potential Failure with the <code>Result</code>
|
|||
|
Type”</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><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>enum Result<T, E> {
|
|||
|
Ok(T),
|
|||
|
Err(E),
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></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 that the standard
|
|||
|
library has defined on it in many different situations where the successful
|
|||
|
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>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">use std::fs::File;
|
|||
|
|
|||
|
fn main() {
|
|||
|
let f = File::open("hello.txt");
|
|||
|
}
|
|||
|
</code></pre></pre>
|
|||
|
<p><span class="caption">Listing 9-3: Opening a file</span></p>
|
|||
|
<p>How do we know <code>File::open</code> returns a <code>Result</code>? We could look at the <a href="../std/index.html">standard
|
|||
|
library API documentation</a><!-- ignore -->, or we could ask
|
|||
|
the compiler! If we give <code>f</code> a type annotation that we know is <em>not</em> the return
|
|||
|
type of the function and then try to compile the code, the compiler will tell
|
|||
|
us that the types don’t match. The error message will then tell us what the
|
|||
|
type of <code>f</code> <em>is</em>. Let’s try it! We know that the return type of <code>File::open</code>
|
|||
|
isn’t of type <code>u32</code>, so let’s change the <code>let f</code> statement to this:</p>
|
|||
|
<pre><code class="language-rust ignore does_not_compile">let f: u32 = File::open("hello.txt");
|
|||
|
</code></pre>
|
|||
|
<p>Attempting to compile now gives us the following output:</p>
|
|||
|
<pre><code class="language-text">error[E0308]: mismatched types
|
|||
|
--> src/main.rs:4:18
|
|||
|
|
|
|||
|
4 | let f: u32 = File::open("hello.txt");
|
|||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum
|
|||
|
`std::result::Result`
|
|||
|
|
|
|||
|
= note: expected type `u32`
|
|||
|
found type `std::result::Result<std::fs::File, std::io::Error>`
|
|||
|
</code></pre>
|
|||
|
<p>This tells us the return type of the <code>File::open</code> function is a <code>Result<T, E></code>.
|
|||
|
The generic parameter <code>T</code> has been filled in here 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>.</p>
|
|||
|
<p>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>f</code> will be
|
|||
|
an instance of <code>Ok</code> that contains a file handle. In the case where it fails,
|
|||
|
the value in <code>f</code> will be an instance of <code>Err</code> that contains more information
|
|||
|
about the kind of error that happened.</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>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust should_panic">use std::fs::File;
|
|||
|
|
|||
|
fn main() {
|
|||
|
let f = File::open("hello.txt");
|
|||
|
|
|||
|
let f = match f {
|
|||
|
Ok(file) => file,
|
|||
|
Err(error) => {
|
|||
|
panic!("Problem opening the file: {:?}", error)
|
|||
|
},
|
|||
|
};
|
|||
|
}
|
|||
|
</code></pre></pre>
|
|||
|
<p><span class="caption">Listing 9-4: Using a <code>match</code> expression to handle the
|
|||
|
<code>Result</code> variants that might be returned</span></p>
|
|||
|
<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>Here we tell Rust that when the result is <code>Ok</code>, 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>f</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-text">thread 'main' panicked at 'Problem opening the file: Error { repr:
|
|||
|
Os { code: 2, message: "No such file or directory" } }', src/main.rs:9:12
|
|||
|
</code></pre>
|
|||
|
<p>As usual, this output tells us exactly what has gone wrong.</p>
|
|||
|
<h3><a class="header" href="#matching-on-different-errors" id="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. What
|
|||
|
we want to do instead is 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 as it did in Listing 9-4. Look
|
|||
|
at Listing 9-5, which adds an inner <code>match</code> expression.</p>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<!-- 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 f = File::open("hello.txt");
|
|||
|
|
|||
|
let f = match f {
|
|||
|
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),
|
|||
|
},
|
|||
|
other_error => panic!("Problem opening the file: {:?}", other_error),
|
|||
|
},
|
|||
|
};
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p><span class="caption">Listing 9-5: Handling different kinds of errors in
|
|||
|
different ways</span></p>
|
|||
|
<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>f</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>
|
|||
|
<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; the <code>Result<T, E></code> type has many methods that accept a closure and are implemented using
|
|||
|
<code>match</code> expressions. Using those methods will make your code more concise. A
|
|||
|
more seasoned Rustacean might write this code instead of Listing 9-5:</p>
|
|||
|
<pre><code class="language-rust ignore">use std::fs::File;
|
|||
|
use std::io::ErrorKind;
|
|||
|
|
|||
|
fn main() {
|
|||
|
let f = 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>
|
|||
|
<h3><a class="header" href="#shortcuts-for-panic-on-error-unwrap-and-expect" id="shortcuts-for-panic-on-error-unwrap-and-expect">Shortcuts for Panic on Error: <code>unwrap</code> and <code>expect</code></a></h3>
|
|||
|
<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 tasks. One of those methods, called <code>unwrap</code>, is a
|
|||
|
shortcut method that is 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>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust should_panic">use std::fs::File;
|
|||
|
|
|||
|
fn main() {
|
|||
|
let f = File::open("hello.txt").unwrap();
|
|||
|
}
|
|||
|
</code></pre></pre>
|
|||
|
<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>
|
|||
|
<pre><code class="language-text">thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {
|
|||
|
repr: Os { code: 2, message: "No such file or directory" } }',
|
|||
|
src/libcore/result.rs:906:4
|
|||
|
</code></pre>
|
|||
|
<p>Another method, <code>expect</code>, which is similar to <code>unwrap</code>, 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>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust should_panic">use std::fs::File;
|
|||
|
|
|||
|
fn main() {
|
|||
|
let f = File::open("hello.txt").expect("Failed to open hello.txt");
|
|||
|
}
|
|||
|
</code></pre></pre>
|
|||
|
<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>
|
|||
|
<pre><code class="language-text">thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
|
|||
|
2, message: "No such file or directory" } }', src/libcore/result.rs:906:4
|
|||
|
</code></pre>
|
|||
|
<p>Because this error message starts with the text we specified, <code>Failed to open hello.txt</code>, it will be easier to find where in the code this error message is
|
|||
|
coming from. If we use <code>unwrap</code> in multiple places, it can take more time to
|
|||
|
figure out exactly which <code>unwrap</code> is causing the panic because all <code>unwrap</code>
|
|||
|
calls that panic print the same message.</p>
|
|||
|
<h3><a class="header" href="#propagating-errors" id="propagating-errors">Propagating Errors</a></h3>
|
|||
|
<p>When you’re writing a function whose implementation calls something that might
|
|||
|
fail, instead of handling the error within this function, 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 this function.</p>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>use std::io;
|
|||
|
use std::io::Read;
|
|||
|
use std::fs::File;
|
|||
|
|
|||
|
fn read_username_from_file() -> Result<String, io::Error> {
|
|||
|
let f = File::open("hello.txt");
|
|||
|
|
|||
|
let mut f = match f {
|
|||
|
Ok(file) => file,
|
|||
|
Err(e) => return Err(e),
|
|||
|
};
|
|||
|
|
|||
|
let mut s = String::new();
|
|||
|
|
|||
|
match f.read_to_string(&mut s) {
|
|||
|
Ok(_) => Ok(s),
|
|||
|
Err(e) => Err(e),
|
|||
|
}
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 9-6: A function that returns errors to the
|
|||
|
calling code using <code>match</code></span></p>
|
|||
|
<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>. 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 username that this function read from the file. If this
|
|||
|
function encounters any problems, the code that calls this function 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 returned with a <code>match</code> similar to the <code>match</code> in
|
|||
|
Listing 9-4, only instead of calling <code>panic!</code> in the <code>Err</code> case, we return
|
|||
|
early from this function and pass the error value from <code>File::open</code> back to the
|
|||
|
calling code as this function’s error value. If <code>File::open</code> succeeds, we store
|
|||
|
the file handle in the variable <code>f</code> and continue.</p>
|
|||
|
<p>Then we create a new <code>String</code> in variable <code>s</code> and call the <code>read_to_string</code>
|
|||
|
method on the file handle in <code>f</code> to read the contents of the file into <code>s</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>s</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>. We
|
|||
|
don’t know what the calling code will 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>
|
|||
|
<h4><a class="header" href="#a-shortcut-for-propagating-errors-the--operator" id="a-shortcut-for-propagating-errors-the--operator">A Shortcut for Propagating Errors: the <code>?</code> Operator</a></h4>
|
|||
|
<p>Listing 9-7 shows an implementation of <code>read_username_from_file</code> that has the
|
|||
|
same functionality as it had in Listing 9-6, but this implementation uses the
|
|||
|
<code>?</code> operator.</p>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>use std::io;
|
|||
|
use std::io::Read;
|
|||
|
use std::fs::File;
|
|||
|
|
|||
|
fn read_username_from_file() -> Result<String, io::Error> {
|
|||
|
let mut f = File::open("hello.txt")?;
|
|||
|
let mut s = String::new();
|
|||
|
f.read_to_string(&mut s)?;
|
|||
|
Ok(s)
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 9-7: A function that returns errors to the
|
|||
|
calling code using the <code>?</code> operator</span></p>
|
|||
|
<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 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 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 and
|
|||
|
the <code>?</code> operator do: 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 errors 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. As
|
|||
|
long as each error type implements the <code>from</code> function to define how to convert
|
|||
|
itself to the returned error type, the <code>?</code> operator takes care of the
|
|||
|
conversion automatically.</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>f</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>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>use std::io;
|
|||
|
use std::io::Read;
|
|||
|
use std::fs::File;
|
|||
|
|
|||
|
fn read_username_from_file() -> Result<String, io::Error> {
|
|||
|
let mut s = String::new();
|
|||
|
|
|||
|
File::open("hello.txt")?.read_to_string(&mut s)?;
|
|||
|
|
|||
|
Ok(s)
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 9-8: Chaining method calls after the <code>?</code>
|
|||
|
operator</span></p>
|
|||
|
<p>We’ve moved the creation of the new <code>String</code> in <code>s</code> to the beginning of the
|
|||
|
function; that part hasn’t changed. Instead of creating a variable <code>f</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 the
|
|||
|
username in <code>s</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>Speaking of different ways to write this function, Listing 9-9 shows that
|
|||
|
there’s a way to make this even shorter.</p>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>use std::io;
|
|||
|
use std::fs;
|
|||
|
|
|||
|
fn read_username_from_file() -> Result<String, io::Error> {
|
|||
|
fs::read_to_string("hello.txt")
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 9-9: Using <code>fs::read_to_string</code> instead of
|
|||
|
opening and then reading the file</span></p>
|
|||
|
<p>Reading a file into a string is a fairly common operation, so Rust 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>
|
|||
|
<h4><a class="header" href="#the--operator-can-be-used-in-functions-that-return-result" id="the--operator-can-be-used-in-functions-that-return-result">The <code>?</code> Operator Can Be Used in Functions That Return <code>Result</code></a></h4>
|
|||
|
<p>The <code>?</code> operator can be used in functions that have a return type of
|
|||
|
<code>Result</code>, because it is defined to work in the same way as the <code>match</code>
|
|||
|
expression we defined in Listing 9-6. The part of the <code>match</code> that requires a
|
|||
|
return type of <code>Result</code> is <code>return Err(e)</code>, so the return type of the function
|
|||
|
can be a <code>Result</code> to be compatible with this <code>return</code>.</p>
|
|||
|
<p>Let’s look at what happens if we use the <code>?</code> operator in the <code>main</code> function,
|
|||
|
which you’ll recall has a return type of <code>()</code>:</p>
|
|||
|
<pre><code class="language-rust ignore does_not_compile">use std::fs::File;
|
|||
|
|
|||
|
fn main() {
|
|||
|
let f = File::open("hello.txt")?;
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p>When we compile this code, we get the following error message:</p>
|
|||
|
<pre><code class="language-text">error[E0277]: the `?` operator can only be used in a function that returns
|
|||
|
`Result` or `Option` (or another type that implements `std::ops::Try`)
|
|||
|
--> src/main.rs:4:13
|
|||
|
|
|
|||
|
4 | let f = File::open("hello.txt")?;
|
|||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a
|
|||
|
function that returns `()`
|
|||
|
|
|
|||
|
= help: the trait `std::ops::Try` is not implemented for `()`
|
|||
|
= note: required by `std::ops::Try::from_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> or <code>Option</code> or another type that implements
|
|||
|
<code>std::ops::Try</code>. When you’re writing code in a function
|
|||
|
that doesn’t return one of these types, and you want to use <code>?</code> when you call other
|
|||
|
functions that return <code>Result<T, E></code>, you have two choices to fix this problem.
|
|||
|
One technique is to change the return type of your function to be <code>Result<T, E></code> if you have no restrictions preventing that. The other technique 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 <code>main</code> function is special, and there are restrictions on what its return
|
|||
|
type must be. One valid return type for main is <code>()</code>, and conveniently, another
|
|||
|
valid return type is <code>Result<T, E></code>, as shown here:</p>
|
|||
|
<pre><code class="language-rust ignore">use std::error::Error;
|
|||
|
use std::fs::File;
|
|||
|
|
|||
|
fn main() -> Result<(), Box<dyn Error>> {
|
|||
|
let f = File::open("hello.txt")?;
|
|||
|
|
|||
|
Ok(())
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p>The <code>Box<dyn Error></code> type is called a trait object, which we’ll talk about in
|
|||
|
the <a href="ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types">“Using Trait Objects that Allow for Values of Different
|
|||
|
Types”</a><!-- ignore --> section in Chapter 17. For now, you can
|
|||
|
read <code>Box<dyn Error></code> to mean “any kind of error.” Using <code>?</code> in a <code>main</code>
|
|||
|
function with this return type is allowed.</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>
|
|||
|
|
|||
|
</main>
|
|||
|
|
|||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|||
|
<!-- Mobile navigation buttons -->
|
|||
|
|
|||
|
<a rel="prev" href="ch09-01-unrecoverable-errors-with-panic.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|||
|
<i class="fa fa-angle-left"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<a rel="next" href="ch09-03-to-panic-or-not-to-panic.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|||
|
<i class="fa fa-angle-right"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
<div style="clear: both"></div>
|
|||
|
</nav>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|||
|
|
|||
|
<a href="ch09-01-unrecoverable-errors-with-panic.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|||
|
<i class="fa fa-angle-left"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<a href="ch09-03-to-panic-or-not-to-panic.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|||
|
<i class="fa fa-angle-right"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
</nav>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<script type="text/javascript">
|
|||
|
window.playpen_copyable = true;
|
|||
|
</script>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
|
|||
|
|
|||
|
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
|
|||
|
<!-- Custom JS scripts -->
|
|||
|
|
|||
|
<script type="text/javascript" src="ferris.js"></script>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
</body>
|
|||
|
</html>
|