3 Like most programming languages, Rust encourages the programmer to handle
4 errors in a particular way. Generally speaking, error handling is divided into
5 two broad categories: exceptions and return values. Rust opts for return
8 In this chapter, we intend to provide a comprehensive treatment of how to deal
9 with errors in Rust. More than that, we will attempt to introduce error handling
10 one piece at a time so that you'll come away with a solid working knowledge of
11 how everything fits together.
13 When done naïvely, error handling in Rust can be verbose and annoying. This
14 chapter will explore those stumbling blocks and demonstrate how to use the
15 standard library to make error handling concise and ergonomic.
19 This chapter is very long, mostly because we start at the very beginning with
20 sum types and combinators, and try to motivate the way Rust does error handling
21 incrementally. As such, programmers with experience in other expressive type
22 systems may want to jump around.
24 * [The Basics](#the-basics)
25 * [Unwrapping explained](#unwrapping-explained)
26 * [The `Option` type](#the-option-type)
27 * [Composing `Option<T>` values](#composing-optiont-values)
28 * [The `Result` type](#the-result-type)
29 * [Parsing integers](#parsing-integers)
30 * [The `Result` type alias idiom](#the-result-type-alias-idiom)
31 * [A brief interlude: unwrapping isn't evil](#a-brief-interlude-unwrapping-isnt-evil)
32 * [Working with multiple error types](#working-with-multiple-error-types)
33 * [Composing `Option` and `Result`](#composing-option-and-result)
34 * [The limits of combinators](#the-limits-of-combinators)
35 * [Early returns](#early-returns)
36 * [The `try!` macro](#the-try-macro)
37 * [Defining your own error type](#defining-your-own-error-type)
38 * [Standard library traits used for error handling](#standard-library-traits-used-for-error-handling)
39 * [The `Error` trait](#the-error-trait)
40 * [The `From` trait](#the-from-trait)
41 * [The real `try!` macro](#the-real-try-macro)
42 * [Composing custom error types](#composing-custom-error-types)
43 * [Advice for library writers](#advice-for-library-writers)
44 * [Case study: A program to read population data](#case-study-a-program-to-read-population-data)
45 * [Initial setup](#initial-setup)
46 * [Argument parsing](#argument-parsing)
47 * [Writing the logic](#writing-the-logic)
48 * [Error handling with `Box<Error>`](#error-handling-with-boxerror)
49 * [Reading from stdin](#reading-from-stdin)
50 * [Error handling with a custom type](#error-handling-with-a-custom-type)
51 * [Adding functionality](#adding-functionality)
52 * [The short story](#the-short-story)
56 You can think of error handling as using *case analysis* to determine whether
57 a computation was successful or not. As you will see, the key to ergonomic error
58 handling is reducing the amount of explicit case analysis the programmer has to
59 do while keeping code composable.
61 Keeping code composable is important, because without that requirement, we
62 could [`panic`](../std/macro.panic!.html) whenever we
63 come across something unexpected. (`panic` causes the current task to unwind,
64 and in most cases, the entire program aborts.) Here's an example:
67 // Guess a number between 1 and 10.
68 // If it matches the number we had in mind, return true. Else, return false.
69 fn guess(n: i32) -> bool {
71 panic!("Invalid number: {}", n);
81 If you try running this code, the program will crash with a message like this:
84 thread '<main>' panicked at 'Invalid number: 11', src/bin/panic-simple.rs:5
87 Here's another example that is slightly less contrived. A program that accepts
88 an integer as an argument, doubles it and prints it.
90 <span id="code-unwrap-double"></span>
96 let mut argv = env::args();
97 let arg: String = argv.nth(1).unwrap(); // error 1
98 let n: i32 = arg.parse().unwrap(); // error 2
99 println!("{}", 2 * n);
103 If you give this program zero arguments (error 1) or if the first argument
104 isn't an integer (error 2), the program will panic just like in the first
107 You can think of this style of error handling as similar to a bull running
108 through a china shop. The bull will get to where it wants to go, but it will
109 trample everything in the process.
111 ## Unwrapping explained
113 In the previous example, we claimed
114 that the program would simply panic if it reached one of the two error
115 conditions, yet, the program does not include an explicit call to `panic` like
116 the first example. This is because the
117 panic is embedded in the calls to `unwrap`.
119 To “unwrap” something in Rust is to say, “Give me the result of the
120 computation, and if there was an error, just panic and stop the program.”
121 It would be better if we just showed the code for unwrapping because it is so
122 simple, but to do that, we will first need to explore the `Option` and `Result`
123 types. Both of these types have a method called `unwrap` defined on them.
125 ### The `Option` type
127 The `Option` type is [defined in the standard library][5]:
136 The `Option` type is a way to use Rust's type system to express the
137 *possibility of absence*. Encoding the possibility of absence into the type
138 system is an important concept because it will cause the compiler to force the
139 programmer to handle that absence. Let's take a look at an example that tries
140 to find a character in a string:
142 <span id="code-option-ex-string-find"></span>
145 // Searches `haystack` for the Unicode character `needle`. If one is found, the
146 // byte offset of the character is returned. Otherwise, `None` is returned.
147 fn find(haystack: &str, needle: char) -> Option<usize> {
148 for (offset, c) in haystack.char_indices() {
157 Notice that when this function finds a matching character, it doesn't just
158 return the `offset`. Instead, it returns `Some(offset)`. `Some` is a variant or
159 a *value constructor* for the `Option` type. You can think of it as a function
160 with the type `fn<T>(value: T) -> Option<T>`. Correspondingly, `None` is also a
161 value constructor, except it has no arguments. You can think of `None` as a
162 function with the type `fn<T>() -> Option<T>`.
164 This might seem like much ado about nothing, but this is only half of the
165 story. The other half is *using* the `find` function we've written. Let's try
166 to use it to find the extension in a file name.
169 # fn find(_: &str, _: char) -> Option<usize> { None }
171 let file_name = "foobar.rs";
172 match find(file_name, '.') {
173 None => println!("No file extension found."),
174 Some(i) => println!("File extension: {}", &file_name[i+1..]),
179 This code uses [pattern matching][1] to do *case
180 analysis* on the `Option<usize>` returned by the `find` function. In fact, case
181 analysis is the only way to get at the value stored inside an `Option<T>`. This
182 means that you, as the programmer, must handle the case when an `Option<T>` is
183 `None` instead of `Some(t)`.
185 But wait, what about `unwrap`,which we used [`previously`](#code-unwrap-double)?
186 There was no case analysis there! Instead, the case analysis was put inside the
187 `unwrap` method for you. You could define it yourself if you want:
189 <span id="code-option-def-unwrap"></span>
198 fn unwrap(self) -> T {
200 Option::Some(val) => val,
202 panic!("called `Option::unwrap()` on a `None` value"),
208 The `unwrap` method *abstracts away the case analysis*. This is precisely the thing
209 that makes `unwrap` ergonomic to use. Unfortunately, that `panic!` means that
210 `unwrap` is not composable: it is the bull in the china shop.
212 ### Composing `Option<T>` values
214 In an [example from before](#code-option-ex-string-find),
215 we saw how to use `find` to discover the extension in a file name. Of course,
216 not all file names have a `.` in them, so it's possible that the file name has
217 no extension. This *possibility of absence* is encoded into the types using
218 `Option<T>`. In other words, the compiler will force us to address the
219 possibility that an extension does not exist. In our case, we just print out a
220 message saying as such.
222 Getting the extension of a file name is a pretty common operation, so it makes
223 sense to put it into a function:
226 # fn find(_: &str, _: char) -> Option<usize> { None }
227 // Returns the extension of the given file name, where the extension is defined
228 // as all characters proceeding the first `.`.
229 // If `file_name` has no `.`, then `None` is returned.
230 fn extension_explicit(file_name: &str) -> Option<&str> {
231 match find(file_name, '.') {
233 Some(i) => Some(&file_name[i+1..]),
238 (Pro-tip: don't use this code. Use the
239 [`extension`](../std/path/struct.Path.html#method.extension)
240 method in the standard library instead.)
242 The code stays simple, but the important thing to notice is that the type of
243 `find` forces us to consider the possibility of absence. This is a good thing
244 because it means the compiler won't let us accidentally forget about the case
245 where a file name doesn't have an extension. On the other hand, doing explicit
246 case analysis like we've done in `extension_explicit` every time can get a bit
249 In fact, the case analysis in `extension_explicit` follows a very common
250 pattern: *map* a function on to the value inside of an `Option<T>`, unless the
251 option is `None`, in which case, just return `None`.
253 Rust has parametric polymorphism, so it is very easy to define a combinator
254 that abstracts this pattern:
256 <span id="code-option-map"></span>
259 fn map<F, T, A>(option: Option<T>, f: F) -> Option<A> where F: FnOnce(T) -> A {
262 Some(value) => Some(f(value)),
267 Indeed, `map` is [defined as a method][2] on `Option<T>` in the standard library.
269 Armed with our new combinator, we can rewrite our `extension_explicit` method
270 to get rid of the case analysis:
273 # fn find(_: &str, _: char) -> Option<usize> { None }
274 // Returns the extension of the given file name, where the extension is defined
275 // as all characters proceeding the first `.`.
276 // If `file_name` has no `.`, then `None` is returned.
277 fn extension(file_name: &str) -> Option<&str> {
278 find(file_name, '.').map(|i| &file_name[i+1..])
282 One other pattern we commonly find is assigning a default value to the case
283 when an `Option` value is `None`. For example, maybe your program assumes that
284 the extension of a file is `rs` even if none is present. As you might imagine,
285 the case analysis for this is not specific to file extensions - it can work
286 with any `Option<T>`:
289 fn unwrap_or<T>(option: Option<T>, default: T) -> T {
292 Some(value) => value,
297 The trick here is that the default value must have the same type as the value
298 that might be inside the `Option<T>`. Using it is dead simple in our case:
301 # fn find(haystack: &str, needle: char) -> Option<usize> {
302 # for (offset, c) in haystack.char_indices() {
304 # return Some(offset);
310 # fn extension(file_name: &str) -> Option<&str> {
311 # find(file_name, '.').map(|i| &file_name[i+1..])
314 assert_eq!(extension("foobar.csv").unwrap_or("rs"), "csv");
315 assert_eq!(extension("foobar").unwrap_or("rs"), "rs");
319 (Note that `unwrap_or` is [defined as a method][3] on `Option<T>` in the
320 standard library, so we use that here instead of the free-standing function we
321 defined above. Don't forget to check out the more general [`unwrap_or_else`][4]
324 There is one more combinator that we think is worth paying special attention to:
325 `and_then`. It makes it easy to compose distinct computations that admit the
326 *possibility of absence*. For example, much of the code in this section is
327 about finding an extension given a file name. In order to do this, you first
328 need the file name which is typically extracted from a file *path*. While most
329 file paths have a file name, not *all* of them do. For example, `.`, `..` or
332 So, we are tasked with the challenge of finding an extension given a file
333 *path*. Let's start with explicit case analysis:
336 # fn extension(file_name: &str) -> Option<&str> { None }
337 fn file_path_ext_explicit(file_path: &str) -> Option<&str> {
338 match file_name(file_path) {
340 Some(name) => match extension(name) {
342 Some(ext) => Some(ext),
347 fn file_name(file_path: &str) -> Option<&str> {
348 // implementation elided
353 You might think that we could just use the `map` combinator to reduce the case
354 analysis, but its type doesn't quite fit. Namely, `map` takes a function that
355 does something only with the inner value. The result of that function is then
356 *always* [rewrapped with `Some`](#code-option-map). Instead, we need something
357 like `map`, but which allows the caller to return another `Option`. Its generic
358 implementation is even simpler than `map`:
361 fn and_then<F, T, A>(option: Option<T>, f: F) -> Option<A>
362 where F: FnOnce(T) -> Option<A> {
365 Some(value) => f(value),
370 Now we can rewrite our `file_path_ext` function without explicit case analysis:
373 # fn extension(file_name: &str) -> Option<&str> { None }
374 # fn file_name(file_path: &str) -> Option<&str> { None }
375 fn file_path_ext(file_path: &str) -> Option<&str> {
376 file_name(file_path).and_then(extension)
380 The `Option` type has many other combinators [defined in the standard
381 library][5]. It is a good idea to skim this list and familiarize
382 yourself with what's available—they can often reduce case analysis
383 for you. Familiarizing yourself with these combinators will pay
384 dividends because many of them are also defined (with similar
385 semantics) for `Result`, which we will talk about next.
387 Combinators make using types like `Option` ergonomic because they reduce
388 explicit case analysis. They are also composable because they permit the caller
389 to handle the possibility of absence in their own way. Methods like `unwrap`
390 remove choices because they will panic if `Option<T>` is `None`.
394 The `Result` type is also
395 [defined in the standard library][6]:
397 <span id="code-result-def"></span>
406 The `Result` type is a richer version of `Option`. Instead of expressing the
407 possibility of *absence* like `Option` does, `Result` expresses the possibility
408 of *error*. Usually, the *error* is used to explain why the execution of some
409 computation failed. This is a strictly more general form of `Option`. Consider
410 the following type alias, which is semantically equivalent to the real
411 `Option<T>` in every way:
414 type Option<T> = Result<T, ()>;
417 This fixes the second type parameter of `Result` to always be `()` (pronounced
418 “unit” or “empty tuple”). Exactly one value inhabits the `()` type: `()`. (Yup,
419 the type and value level terms have the same notation!)
421 The `Result` type is a way of representing one of two possible outcomes in a
422 computation. By convention, one outcome is meant to be expected or “`Ok`” while
423 the other outcome is meant to be unexpected or “`Err`”.
425 Just like `Option`, the `Result` type also has an
428 in the standard library. Let's define it:
431 # enum Result<T, E> { Ok(T), Err(E) }
432 impl<T, E: ::std::fmt::Debug> Result<T, E> {
433 fn unwrap(self) -> T {
435 Result::Ok(val) => val,
437 panic!("called `Result::unwrap()` on an `Err` value: {:?}", err),
443 This is effectively the same as our [definition for
444 `Option::unwrap`](#code-option-def-unwrap), except it includes the
445 error value in the `panic!` message. This makes debugging easier, but
446 it also requires us to add a [`Debug`][8] constraint on the `E` type
447 parameter (which represents our error type). Since the vast majority
448 of types should satisfy the `Debug` constraint, this tends to work out
449 in practice. (`Debug` on a type simply means that there's a reasonable
450 way to print a human readable description of values with that type.)
452 OK, let's move on to an example.
456 The Rust standard library makes converting strings to integers dead simple.
457 It's so easy in fact, that it is very tempting to write something like the
461 fn double_number(number_str: &str) -> i32 {
462 2 * number_str.parse::<i32>().unwrap()
466 let n: i32 = double_number("10");
471 At this point, you should be skeptical of calling `unwrap`. For example, if
472 the string doesn't parse as a number, you'll get a panic:
475 thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', /home/rustbuild/src/rust-buildbot/slave/beta-dist-rustc-linux/build/src/libcore/result.rs:729
478 This is rather unsightly, and if this happened inside a library you're
479 using, you might be understandably annoyed. Instead, we should try to
480 handle the error in our function and let the caller decide what to
481 do. This means changing the return type of `double_number`. But to
482 what? Well, that requires looking at the signature of the [`parse`
483 method][9] in the standard library:
487 fn parse<F: FromStr>(&self) -> Result<F, F::Err>;
491 Hmm. So we at least know that we need to use a `Result`. Certainly, it's
492 possible that this could have returned an `Option`. After all, a string either
493 parses as a number or it doesn't, right? That's certainly a reasonable way to
494 go, but the implementation internally distinguishes *why* the string didn't
495 parse as an integer. (Whether it's an empty string, an invalid digit, too big
496 or too small.) Therefore, using a `Result` makes sense because we want to
497 provide more information than simply “absence.” We want to say *why* the
498 parsing failed. You should try to emulate this line of reasoning when faced
499 with a choice between `Option` and `Result`. If you can provide detailed error
500 information, then you probably should. (We'll see more on this later.)
502 OK, but how do we write our return type? The `parse` method as defined
503 above is generic over all the different number types defined in the
504 standard library. We could (and probably should) also make our
505 function generic, but let's favor explicitness for the moment. We only
506 care about `i32`, so we need to [find its implementation of
507 `FromStr`](../std/primitive.i32.html) (do a `CTRL-F` in your browser
508 for “FromStr”) and look at its [associated type][10] `Err`. We did
509 this so we can find the concrete error type. In this case, it's
510 [`std::num::ParseIntError`](../std/num/struct.ParseIntError.html).
511 Finally, we can rewrite our function:
514 use std::num::ParseIntError;
516 fn double_number(number_str: &str) -> Result<i32, ParseIntError> {
517 match number_str.parse::<i32>() {
519 Err(err) => Err(err),
524 match double_number("10") {
525 Ok(n) => assert_eq!(n, 20),
526 Err(err) => println!("Error: {:?}", err),
531 This is a little better, but now we've written a lot more code! The case
532 analysis has once again bitten us.
534 Combinators to the rescue! Just like `Option`, `Result` has lots of combinators
535 defined as methods. There is a large intersection of common combinators between
536 `Result` and `Option`. In particular, `map` is part of that intersection:
539 use std::num::ParseIntError;
541 fn double_number(number_str: &str) -> Result<i32, ParseIntError> {
542 number_str.parse::<i32>().map(|n| 2 * n)
546 match double_number("10") {
547 Ok(n) => assert_eq!(n, 20),
548 Err(err) => println!("Error: {:?}", err),
553 The usual suspects are all there for `Result`, including
554 [`unwrap_or`](../std/result/enum.Result.html#method.unwrap_or) and
555 [`and_then`](../std/result/enum.Result.html#method.and_then).
556 Additionally, since `Result` has a second type parameter, there are
557 combinators that affect only the error type, such as
558 [`map_err`](../std/result/enum.Result.html#method.map_err) (instead of
559 `map`) and [`or_else`](../std/result/enum.Result.html#method.or_else)
560 (instead of `and_then`).
562 ### The `Result` type alias idiom
564 In the standard library, you may frequently see types like
565 `Result<i32>`. But wait, [we defined `Result`](#code-result-def) to
566 have two type parameters. How can we get away with only specifying
567 one? The key is to define a `Result` type alias that *fixes* one of
568 the type parameters to a particular type. Usually the fixed type is
569 the error type. For example, our previous example parsing integers
570 could be rewritten like this:
573 use std::num::ParseIntError;
576 type Result<T> = result::Result<T, ParseIntError>;
578 fn double_number(number_str: &str) -> Result<i32> {
583 Why would we do this? Well, if we have a lot of functions that could return
584 `ParseIntError`, then it's much more convenient to define an alias that always
585 uses `ParseIntError` so that we don't have to write it out all the time.
587 The most prominent place this idiom is used in the standard library is
588 with [`io::Result`](../std/io/type.Result.html). Typically, one writes
589 `io::Result<T>`, which makes it clear that you're using the `io`
590 module's type alias instead of the plain definition from
591 `std::result`. (This idiom is also used for
592 [`fmt::Result`](../std/fmt/type.Result.html).)
594 ## A brief interlude: unwrapping isn't evil
596 If you've been following along, you might have noticed that I've taken a pretty
597 hard line against calling methods like `unwrap` that could `panic` and abort
598 your program. *Generally speaking*, this is good advice.
600 However, `unwrap` can still be used judiciously. What exactly justifies use of
601 `unwrap` is somewhat of a grey area and reasonable people can disagree. I'll
602 summarize some of my *opinions* on the matter.
604 * **In examples and quick 'n' dirty code.** Sometimes you're writing examples
605 or a quick program, and error handling simply isn't important. Beating the
606 convenience of `unwrap` can be hard in such scenarios, so it is very
608 * **When panicking indicates a bug in the program.** When the invariants of
609 your code should prevent a certain case from happening (like, say, popping
610 from an empty stack), then panicking can be permissible. This is because it
611 exposes a bug in your program. This can be explicit, like from an `assert!`
612 failing, or it could be because your index into an array was out of bounds.
614 This is probably not an exhaustive list. Moreover, when using an
615 `Option`, it is often better to use its
616 [`expect`](../std/option/enum.Option.html#method.expect)
617 method. `expect` does exactly the same thing as `unwrap`, except it
618 prints a message you give to `expect`. This makes the resulting panic
619 a bit nicer to deal with, since it will show your message instead of
620 “called unwrap on a `None` value.”
622 My advice boils down to this: use good judgment. There's a reason why the words
623 “never do X” or “Y is considered harmful” don't appear in my writing. There are
624 trade offs to all things, and it is up to you as the programmer to determine
625 what is acceptable for your use cases. My goal is only to help you evaluate
626 trade offs as accurately as possible.
628 Now that we've covered the basics of error handling in Rust, and
629 explained unwrapping, let's start exploring more of the standard
632 # Working with multiple error types
634 Thus far, we've looked at error handling where everything was either an
635 `Option<T>` or a `Result<T, SomeError>`. But what happens when you have both an
636 `Option` and a `Result`? Or what if you have a `Result<T, Error1>` and a
637 `Result<T, Error2>`? Handling *composition of distinct error types* is the next
638 challenge in front of us, and it will be the major theme throughout the rest of
641 ## Composing `Option` and `Result`
643 So far, I've talked about combinators defined for `Option` and combinators
644 defined for `Result`. We can use these combinators to compose results of
645 different computations without doing explicit case analysis.
647 Of course, in real code, things aren't always as clean. Sometimes you have a
648 mix of `Option` and `Result` types. Must we resort to explicit case analysis,
649 or can we continue using combinators?
651 For now, let's revisit one of the first examples in this chapter:
657 let mut argv = env::args();
658 let arg: String = argv.nth(1).unwrap(); // error 1
659 let n: i32 = arg.parse().unwrap(); // error 2
660 println!("{}", 2 * n);
664 Given our new found knowledge of `Option`, `Result` and their various
665 combinators, we should try to rewrite this so that errors are handled properly
666 and the program doesn't panic if there's an error.
668 The tricky aspect here is that `argv.nth(1)` produces an `Option` while
669 `arg.parse()` produces a `Result`. These aren't directly composable. When faced
670 with both an `Option` and a `Result`, the solution is *usually* to convert the
671 `Option` to a `Result`. In our case, the absence of a command line parameter
672 (from `env::args()`) means the user didn't invoke the program correctly. We
673 could just use a `String` to describe the error. Let's try:
675 <span id="code-error-double-string"></span>
680 fn double_arg(mut argv: env::Args) -> Result<i32, String> {
682 .ok_or("Please give at least one argument".to_owned())
683 .and_then(|arg| arg.parse::<i32>().map_err(|err| err.to_string()))
687 match double_arg(env::args()) {
688 Ok(n) => println!("{}", n),
689 Err(err) => println!("Error: {}", err),
694 There are a couple new things in this example. The first is the use of the
695 [`Option::ok_or`](../std/option/enum.Option.html#method.ok_or)
696 combinator. This is one way to convert an `Option` into a `Result`. The
697 conversion requires you to specify what error to use if `Option` is `None`.
698 Like the other combinators we've seen, its definition is very simple:
701 fn ok_or<T, E>(option: Option<T>, err: E) -> Result<T, E> {
703 Some(val) => Ok(val),
709 The other new combinator used here is
710 [`Result::map_err`](../std/result/enum.Result.html#method.map_err).
711 This is just like `Result::map`, except it maps a function on to the *error*
712 portion of a `Result` value. If the `Result` is an `Ok(...)` value, then it is
715 We use `map_err` here because it is necessary for the error types to remain
716 the same (because of our use of `and_then`). Since we chose to convert the
717 `Option<String>` (from `argv.nth(1)`) to a `Result<String, String>`, we must
718 also convert the `ParseIntError` from `arg.parse()` to a `String`.
720 ## The limits of combinators
722 Doing IO and parsing input is a very common task, and it's one that I
723 personally have done a lot of in Rust. Therefore, we will use (and continue to
724 use) IO and various parsing routines to exemplify error handling.
726 Let's start simple. We are tasked with opening a file, reading all of its
727 contents and converting its contents to a number. Then we multiply it by `2`
728 and print the output.
730 Although I've tried to convince you not to use `unwrap`, it can be useful
731 to first write your code using `unwrap`. It allows you to focus on your problem
732 instead of the error handling, and it exposes the points where proper error
733 handling need to occur. Let's start there so we can get a handle on the code,
734 and then refactor it to use better error handling.
741 fn file_double<P: AsRef<Path>>(file_path: P) -> i32 {
742 let mut file = File::open(file_path).unwrap(); // error 1
743 let mut contents = String::new();
744 file.read_to_string(&mut contents).unwrap(); // error 2
745 let n: i32 = contents.trim().parse().unwrap(); // error 3
750 let doubled = file_double("foobar");
751 println!("{}", doubled);
755 (N.B. The `AsRef<Path>` is used because those are the
757 `std::fs::File::open`](../std/fs/struct.File.html#method.open).
758 This makes it ergonomic to use any kind of string as a file path.)
760 There are three different errors that can occur here:
762 1. A problem opening the file.
763 2. A problem reading data from the file.
764 3. A problem parsing the data as a number.
766 The first two problems are described via the
767 [`std::io::Error`](../std/io/struct.Error.html) type. We know this
768 because of the return types of
769 [`std::fs::File::open`](../std/fs/struct.File.html#method.open) and
770 [`std::io::Read::read_to_string`](../std/io/trait.Read.html#method.read_to_string).
771 (Note that they both use the [`Result` type alias
772 idiom](#the-result-type-alias-idiom) described previously. If you
773 click on the `Result` type, you'll [see the type
774 alias](../std/io/type.Result.html), and consequently, the underlying
775 `io::Error` type.) The third problem is described by the
776 [`std::num::ParseIntError`](../std/num/struct.ParseIntError.html)
777 type. The `io::Error` type in particular is *pervasive* throughout the
778 standard library. You will see it again and again.
780 Let's start the process of refactoring the `file_double` function. To make this
781 function composable with other components of the program, it should *not* panic
782 if any of the above error conditions are met. Effectively, this means that the
783 function should *return an error* if any of its operations fail. Our problem is
784 that the return type of `file_double` is `i32`, which does not give us any
785 useful way of reporting an error. Thus, we must start by changing the return
786 type from `i32` to something else.
788 The first thing we need to decide: should we use `Option` or `Result`? We
789 certainly could use `Option` very easily. If any of the three errors occur, we
790 could simply return `None`. This will work *and it is better than panicking*,
791 but we can do a lot better. Instead, we should pass some detail about the error
792 that occurred. Since we want to express the *possibility of error*, we should
793 use `Result<i32, E>`. But what should `E` be? Since two *different* types of
794 errors can occur, we need to convert them to a common type. One such type is
795 `String`. Let's see how that impacts our code:
802 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
803 File::open(file_path)
804 .map_err(|err| err.to_string())
805 .and_then(|mut file| {
806 let mut contents = String::new();
807 file.read_to_string(&mut contents)
808 .map_err(|err| err.to_string())
811 .and_then(|contents| {
812 contents.trim().parse::<i32>()
813 .map_err(|err| err.to_string())
819 match file_double("foobar") {
820 Ok(n) => println!("{}", n),
821 Err(err) => println!("Error: {}", err),
826 This code looks a bit hairy. It can take quite a bit of practice before code
827 like this becomes easy to write. The way we write it is by *following the
828 types*. As soon as we changed the return type of `file_double` to
829 `Result<i32, String>`, we had to start looking for the right combinators. In
830 this case, we only used three different combinators: `and_then`, `map` and
833 `and_then` is used to chain multiple computations where each computation could
834 return an error. After opening the file, there are two more computations that
835 could fail: reading from the file and parsing the contents as a number.
836 Correspondingly, there are two calls to `and_then`.
838 `map` is used to apply a function to the `Ok(...)` value of a `Result`. For
839 example, the very last call to `map` multiplies the `Ok(...)` value (which is
840 an `i32`) by `2`. If an error had occurred before that point, this operation
841 would have been skipped because of how `map` is defined.
843 `map_err` is the trick that makes all of this work. `map_err` is just like
844 `map`, except it applies a function to the `Err(...)` value of a `Result`. In
845 this case, we want to convert all of our errors to one type: `String`. Since
846 both `io::Error` and `num::ParseIntError` implement `ToString`, we can call the
847 `to_string()` method to convert them.
849 With all of that said, the code is still hairy. Mastering use of combinators is
850 important, but they have their limits. Let's try a different approach: early
855 I'd like to take the code from the previous section and rewrite it using *early
856 returns*. Early returns let you exit the function early. We can't return early
857 in `file_double` from inside another closure, so we'll need to revert back to
858 explicit case analysis.
865 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
866 let mut file = match File::open(file_path) {
868 Err(err) => return Err(err.to_string()),
870 let mut contents = String::new();
871 if let Err(err) = file.read_to_string(&mut contents) {
872 return Err(err.to_string());
874 let n: i32 = match contents.trim().parse() {
876 Err(err) => return Err(err.to_string()),
882 match file_double("foobar") {
883 Ok(n) => println!("{}", n),
884 Err(err) => println!("Error: {}", err),
889 Reasonable people can disagree over whether this code is better that the code
890 that uses combinators, but if you aren't familiar with the combinator approach,
891 this code looks simpler to read to me. It uses explicit case analysis with
892 `match` and `if let`. If an error occurs, it simply stops executing the
893 function and returns the error (by converting it to a string).
895 Isn't this a step backwards though? Previously, we said that the key to
896 ergonomic error handling is reducing explicit case analysis, yet we've reverted
897 back to explicit case analysis here. It turns out, there are *multiple* ways to
898 reduce explicit case analysis. Combinators aren't the only way.
902 A cornerstone of error handling in Rust is the `try!` macro. The `try!` macro
903 abstracts case analysis just like combinators, but unlike combinators, it also
904 abstracts *control flow*. Namely, it can abstract the *early return* pattern
907 Here is a simplified definition of a `try!` macro:
909 <span id="code-try-def-simple"></span>
913 ($e:expr) => (match $e {
915 Err(err) => return Err(err),
920 (The [real definition](../std/macro.try!.html) is a bit more
921 sophisticated. We will address that later.)
923 Using the `try!` macro makes it very easy to simplify our last example. Since
924 it does the case analysis and the early return for us, we get tighter code that
932 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
933 let mut file = try!(File::open(file_path).map_err(|e| e.to_string()));
934 let mut contents = String::new();
935 try!(file.read_to_string(&mut contents).map_err(|e| e.to_string()));
936 let n = try!(contents.trim().parse::<i32>().map_err(|e| e.to_string()));
941 match file_double("foobar") {
942 Ok(n) => println!("{}", n),
943 Err(err) => println!("Error: {}", err),
948 The `map_err` calls are still necessary given
949 [our definition of `try!`](#code-try-def-simple).
950 This is because the error types still need to be converted to `String`.
951 The good news is that we will soon learn how to remove those `map_err` calls!
952 The bad news is that we will need to learn a bit more about a couple important
953 traits in the standard library before we can remove the `map_err` calls.
955 ## Defining your own error type
957 Before we dive into some of the standard library error traits, I'd like to wrap
958 up this section by removing the use of `String` as our error type in the
961 Using `String` as we did in our previous examples is convenient because it's
962 easy to convert errors to strings, or even make up your own errors as strings
963 on the spot. However, using `String` for your errors has some downsides.
965 The first downside is that the error messages tend to clutter your
966 code. It's possible to define the error messages elsewhere, but unless
967 you're unusually disciplined, it is very tempting to embed the error
968 message into your code. Indeed, we did exactly this in a [previous
969 example](#code-error-double-string).
971 The second and more important downside is that `String`s are *lossy*. That is,
972 if all errors are converted to strings, then the errors we pass to the caller
973 become completely opaque. The only reasonable thing the caller can do with a
974 `String` error is show it to the user. Certainly, inspecting the string to
975 determine the type of error is not robust. (Admittedly, this downside is far
976 more important inside of a library as opposed to, say, an application.)
978 For example, the `io::Error` type embeds an
979 [`io::ErrorKind`](../std/io/enum.ErrorKind.html),
980 which is *structured data* that represents what went wrong during an IO
981 operation. This is important because you might want to react differently
982 depending on the error. (e.g., A `BrokenPipe` error might mean quitting your
983 program gracefully while a `NotFound` error might mean exiting with an error
984 code and showing an error to the user.) With `io::ErrorKind`, the caller can
985 examine the type of an error with case analysis, which is strictly superior
986 to trying to tease out the details of an error inside of a `String`.
988 Instead of using a `String` as an error type in our previous example of reading
989 an integer from a file, we can define our own error type that represents errors
990 with *structured data*. We endeavor to not drop information from underlying
991 errors in case the caller wants to inspect the details.
993 The ideal way to represent *one of many possibilities* is to define our own
994 sum type using `enum`. In our case, an error is either an `io::Error` or a
995 `num::ParseIntError`, so a natural definition arises:
1001 // We derive `Debug` because all types should probably derive `Debug`.
1002 // This gives us a reasonable human readable description of `CliError` values.
1006 Parse(num::ParseIntError),
1010 Tweaking our code is very easy. Instead of converting errors to strings, we
1011 simply convert them to our `CliError` type using the corresponding value
1016 # enum CliError { Io(::std::io::Error), Parse(::std::num::ParseIntError) }
1019 use std::path::Path;
1021 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
1022 let mut file = try!(File::open(file_path).map_err(CliError::Io));
1023 let mut contents = String::new();
1024 try!(file.read_to_string(&mut contents).map_err(CliError::Io));
1025 let n: i32 = try!(contents.trim().parse().map_err(CliError::Parse));
1030 match file_double("foobar") {
1031 Ok(n) => println!("{}", n),
1032 Err(err) => println!("Error: {:?}", err),
1037 The only change here is switching `map_err(|e| e.to_string())` (which converts
1038 errors to strings) to `map_err(CliError::Io)` or `map_err(CliError::Parse)`.
1039 The *caller* gets to decide the level of detail to report to the user. In
1040 effect, using a `String` as an error type removes choices from the caller while
1041 using a custom `enum` error type like `CliError` gives the caller all of the
1042 conveniences as before in addition to *structured data* describing the error.
1044 A rule of thumb is to define your own error type, but a `String` error type
1045 will do in a pinch, particularly if you're writing an application. If you're
1046 writing a library, defining your own error type should be strongly preferred so
1047 that you don't remove choices from the caller unnecessarily.
1049 # Standard library traits used for error handling
1051 The standard library defines two integral traits for error handling:
1052 [`std::error::Error`](../std/error/trait.Error.html) and
1053 [`std::convert::From`](../std/convert/trait.From.html). While `Error`
1054 is designed specifically for generically describing errors, the `From`
1055 trait serves a more general role for converting values between two
1058 ## The `Error` trait
1060 The `Error` trait is [defined in the standard
1061 library](../std/error/trait.Error.html):
1064 use std::fmt::{Debug, Display};
1066 trait Error: Debug + Display {
1067 /// A short description of the error.
1068 fn description(&self) -> &str;
1070 /// The lower level cause of this error, if any.
1071 fn cause(&self) -> Option<&Error> { None }
1075 This trait is super generic because it is meant to be implemented for *all*
1076 types that represent errors. This will prove useful for writing composable code
1077 as we'll see later. Otherwise, the trait allows you to do at least the
1080 * Obtain a `Debug` representation of the error.
1081 * Obtain a user-facing `Display` representation of the error.
1082 * Obtain a short description of the error (via the `description` method).
1083 * Inspect the causal chain of an error, if one exists (via the `cause` method).
1085 The first two are a result of `Error` requiring impls for both `Debug` and
1086 `Display`. The latter two are from the two methods defined on `Error`. The
1087 power of `Error` comes from the fact that all error types impl `Error`, which
1088 means errors can be existentially quantified as a
1089 [trait object](../book/trait-objects.html).
1090 This manifests as either `Box<Error>` or `&Error`. Indeed, the `cause` method
1091 returns an `&Error`, which is itself a trait object. We'll revisit the
1092 `Error` trait's utility as a trait object later.
1094 For now, it suffices to show an example implementing the `Error` trait. Let's
1095 use the error type we defined in the
1096 [previous section](#defining-your-own-error-type):
1102 // We derive `Debug` because all types should probably derive `Debug`.
1103 // This gives us a reasonable human readable description of `CliError` values.
1107 Parse(num::ParseIntError),
1111 This particular error type represents the possibility of two types of errors
1112 occurring: an error dealing with I/O or an error converting a string to a
1113 number. The error could represent as many error types as you want by adding new
1114 variants to the `enum` definition.
1116 Implementing `Error` is pretty straight-forward. It's mostly going to be a lot
1117 explicit case analysis.
1123 impl fmt::Display for CliError {
1124 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1126 // Both underlying errors already impl `Display`, so we defer to
1127 // their implementations.
1128 CliError::Io(ref err) => write!(f, "IO error: {}", err),
1129 CliError::Parse(ref err) => write!(f, "Parse error: {}", err),
1134 impl error::Error for CliError {
1135 fn description(&self) -> &str {
1136 // Both underlying errors already impl `Error`, so we defer to their
1139 CliError::Io(ref err) => err.description(),
1140 CliError::Parse(ref err) => err.description(),
1144 fn cause(&self) -> Option<&error::Error> {
1146 // N.B. Both of these implicitly cast `err` from their concrete
1147 // types (either `&io::Error` or `&num::ParseIntError`)
1148 // to a trait object `&Error`. This works because both error types
1149 // implement `Error`.
1150 CliError::Io(ref err) => Some(err),
1151 CliError::Parse(ref err) => Some(err),
1157 We note that this is a very typical implementation of `Error`: match on your
1158 different error types and satisfy the contracts defined for `description` and
1163 The `std::convert::From` trait is
1164 [defined in the standard
1165 library](../std/convert/trait.From.html):
1167 <span id="code-from-def"></span>
1175 Deliciously simple, yes? `From` is very useful because it gives us a generic
1176 way to talk about conversion *from* a particular type `T` to some other type
1177 (in this case, “some other type” is the subject of the impl, or `Self`).
1178 The crux of `From` is the
1179 [set of implementations provided by the standard
1180 library](../std/convert/trait.From.html).
1182 Here are a few simple examples demonstrating how `From` works:
1185 let string: String = From::from("foo");
1186 let bytes: Vec<u8> = From::from("foo");
1187 let cow: ::std::borrow::Cow<str> = From::from("foo");
1190 OK, so `From` is useful for converting between strings. But what about errors?
1191 It turns out, there is one critical impl:
1194 impl<'a, E: Error + 'a> From<E> for Box<Error + 'a>
1197 This impl says that for *any* type that impls `Error`, we can convert it to a
1198 trait object `Box<Error>`. This may not seem terribly surprising, but it is
1199 useful in a generic context.
1201 Remember the two errors we were dealing with previously? Specifically,
1202 `io::Error` and `num::ParseIntError`. Since both impl `Error`, they work with
1206 use std::error::Error;
1211 // We have to jump through some hoops to actually get error values.
1212 let io_err: io::Error = io::Error::last_os_error();
1213 let parse_err: num::ParseIntError = "not a number".parse::<i32>().unwrap_err();
1215 // OK, here are the conversions.
1216 let err1: Box<Error> = From::from(io_err);
1217 let err2: Box<Error> = From::from(parse_err);
1220 There is a really important pattern to recognize here. Both `err1` and `err2`
1221 have the *same type*. This is because they are existentially quantified types,
1222 or trait objects. In particular, their underlying type is *erased* from the
1223 compiler's knowledge, so it truly sees `err1` and `err2` as exactly the same.
1224 Additionally, we constructed `err1` and `err2` using precisely the same
1225 function call: `From::from`. This is because `From::from` is overloaded on both
1226 its argument and its return type.
1228 This pattern is important because it solves a problem we had earlier: it gives
1229 us a way to reliably convert errors to the same type using the same function.
1231 Time to revisit an old friend; the `try!` macro.
1233 ## The real `try!` macro
1235 Previously, we presented this definition of `try!`:
1239 ($e:expr) => (match $e {
1241 Err(err) => return Err(err),
1246 This is not its real definition. Its real definition is
1247 [in the standard library](../std/macro.try!.html):
1249 <span id="code-try-def"></span>
1253 ($e:expr) => (match $e {
1255 Err(err) => return Err(::std::convert::From::from(err)),
1260 There's one tiny but powerful change: the error value is passed through
1261 `From::from`. This makes the `try!` macro a lot more powerful because it gives
1262 you automatic type conversion for free.
1264 Armed with our more powerful `try!` macro, let's take a look at code we wrote
1265 previously to read a file and convert its contents to an integer:
1270 use std::path::Path;
1272 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
1273 let mut file = try!(File::open(file_path).map_err(|e| e.to_string()));
1274 let mut contents = String::new();
1275 try!(file.read_to_string(&mut contents).map_err(|e| e.to_string()));
1276 let n = try!(contents.trim().parse::<i32>().map_err(|e| e.to_string()));
1281 Earlier, we promised that we could get rid of the `map_err` calls. Indeed, all
1282 we have to do is pick a type that `From` works with. As we saw in the previous
1283 section, `From` has an impl that lets it convert any error type into a
1287 use std::error::Error;
1290 use std::path::Path;
1292 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, Box<Error>> {
1293 let mut file = try!(File::open(file_path));
1294 let mut contents = String::new();
1295 try!(file.read_to_string(&mut contents));
1296 let n = try!(contents.trim().parse::<i32>());
1301 We are getting very close to ideal error handling. Our code has very little
1302 overhead as a result from error handling because the `try!` macro encapsulates
1303 three things simultaneously:
1307 3. Error type conversion.
1309 When all three things are combined, we get code that is unencumbered by
1310 combinators, calls to `unwrap` or case analysis.
1312 There's one little nit left: the `Box<Error>` type is *opaque*. If we
1313 return a `Box<Error>` to the caller, the caller can't (easily) inspect
1314 underlying error type. The situation is certainly better than `String`
1315 because the caller can call methods like
1316 [`description`](../std/error/trait.Error.html#tymethod.description)
1317 and [`cause`](../std/error/trait.Error.html#method.cause), but the
1318 limitation remains: `Box<Error>` is opaque. (N.B. This isn't entirely
1319 true because Rust does have runtime reflection, which is useful in
1320 some scenarios that are [beyond the scope of this
1321 chapter](https://crates.io/crates/error).)
1323 It's time to revisit our custom `CliError` type and tie everything together.
1325 ## Composing custom error types
1327 In the last section, we looked at the real `try!` macro and how it does
1328 automatic type conversion for us by calling `From::from` on the error value.
1329 In particular, we converted errors to `Box<Error>`, which works, but the type
1330 is opaque to callers.
1332 To fix this, we use the same remedy that we're already familiar with: a custom
1333 error type. Once again, here is the code that reads the contents of a file and
1334 converts it to an integer:
1338 use std::io::{self, Read};
1340 use std::path::Path;
1342 // We derive `Debug` because all types should probably derive `Debug`.
1343 // This gives us a reasonable human readable description of `CliError` values.
1347 Parse(num::ParseIntError),
1350 fn file_double_verbose<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
1351 let mut file = try!(File::open(file_path).map_err(CliError::Io));
1352 let mut contents = String::new();
1353 try!(file.read_to_string(&mut contents).map_err(CliError::Io));
1354 let n: i32 = try!(contents.trim().parse().map_err(CliError::Parse));
1359 Notice that we still have the calls to `map_err`. Why? Well, recall the
1360 definitions of [`try!`](#code-try-def) and [`From`](#code-from-def). The
1361 problem is that there is no `From` impl that allows us to convert from error
1362 types like `io::Error` and `num::ParseIntError` to our own custom `CliError`.
1363 Of course, it is easy to fix this! Since we defined `CliError`, we can impl
1368 # enum CliError { Io(io::Error), Parse(num::ParseIntError) }
1372 impl From<io::Error> for CliError {
1373 fn from(err: io::Error) -> CliError {
1378 impl From<num::ParseIntError> for CliError {
1379 fn from(err: num::ParseIntError) -> CliError {
1380 CliError::Parse(err)
1385 All these impls are doing is teaching `From` how to create a `CliError` from
1386 other error types. In our case, construction is as simple as invoking the
1387 corresponding value constructor. Indeed, it is *typically* this easy.
1389 We can finally rewrite `file_double`:
1394 # enum CliError { Io(::std::io::Error), Parse(::std::num::ParseIntError) }
1395 # impl From<io::Error> for CliError {
1396 # fn from(err: io::Error) -> CliError { CliError::Io(err) }
1398 # impl From<num::ParseIntError> for CliError {
1399 # fn from(err: num::ParseIntError) -> CliError { CliError::Parse(err) }
1404 use std::path::Path;
1406 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
1407 let mut file = try!(File::open(file_path));
1408 let mut contents = String::new();
1409 try!(file.read_to_string(&mut contents));
1410 let n: i32 = try!(contents.trim().parse());
1415 The only thing we did here was remove the calls to `map_err`. They are no
1416 longer needed because the `try!` macro invokes `From::from` on the error value.
1417 This works because we've provided `From` impls for all the error types that
1420 If we modified our `file_double` function to perform some other operation, say,
1421 convert a string to a float, then we'd need to add a new variant to our error
1430 ParseInt(num::ParseIntError),
1431 ParseFloat(num::ParseFloatError),
1435 And add a new `From` impl:
1439 # Io(::std::io::Error),
1440 # ParseInt(num::ParseIntError),
1441 # ParseFloat(num::ParseFloatError),
1446 impl From<num::ParseFloatError> for CliError {
1447 fn from(err: num::ParseFloatError) -> CliError {
1448 CliError::ParseFloat(err)
1455 ## Advice for library writers
1457 If your library needs to report custom errors, then you should
1458 probably define your own error type. It's up to you whether or not to
1459 expose its representation (like
1460 [`ErrorKind`](../std/io/enum.ErrorKind.html)) or keep it hidden (like
1461 [`ParseIntError`](../std/num/struct.ParseIntError.html)). Regardless
1462 of how you do it, it's usually good practice to at least provide some
1463 information about the error beyond just its `String`
1464 representation. But certainly, this will vary depending on use cases.
1466 At a minimum, you should probably implement the
1467 [`Error`](../std/error/trait.Error.html)
1468 trait. This will give users of your library some minimum flexibility for
1469 [composing errors](#the-real-try-macro). Implementing the `Error` trait also
1470 means that users are guaranteed the ability to obtain a string representation
1471 of an error (because it requires impls for both `fmt::Debug` and
1474 Beyond that, it can also be useful to provide implementations of `From` on your
1475 error types. This allows you (the library author) and your users to
1476 [compose more detailed errors](#composing-custom-error-types). For example,
1477 [`csv::Error`](http://burntsushi.net/rustdoc/csv/enum.Error.html)
1478 provides `From` impls for both `io::Error` and `byteorder::Error`.
1480 Finally, depending on your tastes, you may also want to define a
1481 [`Result` type alias](#the-result-type-alias-idiom), particularly if your
1482 library defines a single error type. This is used in the standard library
1483 for [`io::Result`](../std/io/type.Result.html)
1484 and [`fmt::Result`](../std/fmt/type.Result.html).
1486 # Case study: A program to read population data
1488 This chapter was long, and depending on your background, it might be
1489 rather dense. While there is plenty of example code to go along with
1490 the prose, most of it was specifically designed to be pedagogical. So,
1491 we're going to do something new: a case study.
1493 For this, we're going to build up a command line program that lets you
1494 query world population data. The objective is simple: you give it a location
1495 and it will tell you the population. Despite the simplicity, there is a lot
1498 The data we'll be using comes from the [Data Science
1499 Toolkit][11]. I've prepared some data from it for this exercise. You
1500 can either grab the [world population data][12] (41MB gzip compressed,
1501 145MB uncompressed) or just the [US population data][13] (2.2MB gzip
1502 compressed, 7.2MB uncompressed).
1504 Up until now, we've kept the code limited to Rust's standard library. For a real
1505 task like this though, we'll want to at least use something to parse CSV data,
1506 parse the program arguments and decode that stuff into Rust types automatically. For that, we'll use the
1507 [`csv`](https://crates.io/crates/csv),
1508 and [`rustc-serialize`](https://crates.io/crates/rustc-serialize) crates.
1512 We're not going to spend a lot of time on setting up a project with
1513 Cargo because it is already covered well in [the Cargo
1514 chapter](../book/hello-cargo.html) and [Cargo's documentation][14].
1516 To get started from scratch, run `cargo new --bin city-pop` and make sure your
1517 `Cargo.toml` looks something like this:
1523 authors = ["Andrew Gallant <jamslam@gmail.com>"]
1530 rustc-serialize = "0.*"
1534 You should already be able to run:
1537 cargo build --release
1538 ./target/release/city-pop
1539 # Outputs: Hello, world!
1544 Let's get argument parsing out of the way. We won't go into too much
1545 detail on Getopts, but there is [some good documentation][15]
1546 describing it. The short story is that Getopts generates an argument
1547 parser and a help message from a vector of options (The fact that it
1548 is a vector is hidden behind a struct and a set of methods). Once the
1549 parsing is done, we can decode the program arguments into a Rust
1550 struct. From there, we can get information about the flags, for
1551 instance, whether they were passed in, and what arguments they
1552 had. Here's our program with the appropriate `extern crate`
1553 statements, and the basic argument setup for Getopts:
1556 extern crate getopts;
1557 extern crate rustc_serialize;
1559 use getopts::Options;
1562 fn print_usage(program: &str, opts: Options) {
1563 println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
1567 let args: Vec<String> = env::args().collect();
1568 let program = args[0].clone();
1570 let mut opts = Options::new();
1571 opts.optflag("h", "help", "Show this usage message.");
1573 let matches = match opts.parse(&args[1..]) {
1575 Err(e) => { panic!(e.to_string()) }
1577 if matches.opt_present("h") {
1578 print_usage(&program, opts);
1581 let data_path = args[1].clone();
1582 let city = args[2].clone();
1584 // Do stuff with information
1588 First, we get a vector of the arguments passed into our program. We
1589 then store the first one, knowing that it is our program's name. Once
1590 that's done, we set up our argument flags, in this case a simplistic
1591 help message flag. Once we have the argument flags set up, we use
1592 `Options.parse` to parse the argument vector (starting from index one,
1593 because index 0 is the program name). If this was successful, we
1594 assign matches to the parsed object, if not, we panic. Once past that,
1595 we test if the user passed in the help flag, and if so print the usage
1596 message. The option help messages are constructed by Getopts, so all
1597 we have to do to print the usage message is tell it what we want it to
1598 print for the program name and template. If the user has not passed in
1599 the help flag, we assign the proper variables to their corresponding
1602 ## Writing the logic
1604 We all write code differently, but error handling is usually the last thing we
1605 want to think about. This isn't great for the overall design of a program, but
1606 it can be useful for rapid prototyping. Because Rust forces us to be explicit
1607 about error handling (by making us call `unwrap`), it is easy to see which
1608 parts of our program can cause errors.
1610 In this case study, the logic is really simple. All we need to do is parse the
1611 CSV data given to us and print out a field in matching rows. Let's do it. (Make
1612 sure to add `extern crate csv;` to the top of your file.)
1615 // This struct represents the data in each row of the CSV file.
1616 // Type based decoding absolves us of a lot of the nitty gritty error
1617 // handling, like parsing strings as integers or floats.
1618 #[derive(Debug, RustcDecodable)]
1622 accent_city: String,
1625 // Not every row has data for the population, latitude or longitude!
1626 // So we express them as `Option` types, which admits the possibility of
1627 // absence. The CSV parser will fill in the correct value for us.
1628 population: Option<u64>,
1629 latitude: Option<f64>,
1630 longitude: Option<f64>,
1633 fn print_usage(program: &str, opts: Options) {
1634 println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
1638 let args: Vec<String> = env::args().collect();
1639 let program = args[0].clone();
1641 let mut opts = Options::new();
1642 opts.optflag("h", "help", "Show this usage message.");
1644 let matches = match opts.parse(&args[1..]) {
1646 Err(e) => { panic!(e.to_string()) }
1649 if matches.opt_present("h") {
1650 print_usage(&program, opts);
1654 let data_file = args[1].clone();
1655 let data_path = Path::new(&data_file);
1656 let city = args[2].clone();
1658 let file = fs::File::open(data_path).unwrap();
1659 let mut rdr = csv::Reader::from_reader(file);
1661 for row in rdr.decode::<Row>() {
1662 let row = row.unwrap();
1664 if row.city == city {
1665 println!("{}, {}: {:?}",
1666 row.city, row.country,
1667 row.population.expect("population count"));
1673 Let's outline the errors. We can start with the obvious: the three places that
1676 1. [`fs::File::open`](../std/fs/struct.File.html#method.open)
1678 [`io::Error`](../std/io/struct.Error.html).
1679 2. [`csv::Reader::decode`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.decode)
1680 decodes one record at a time, and
1682 record](http://burntsushi.net/rustdoc/csv/struct.DecodedRecords.html)
1683 (look at the `Item` associated type on the `Iterator` impl)
1685 [`csv::Error`](http://burntsushi.net/rustdoc/csv/enum.Error.html).
1686 3. If `row.population` is `None`, then calling `expect` will panic.
1688 Are there any others? What if we can't find a matching city? Tools like `grep`
1689 will return an error code, so we probably should too. So we have logic errors
1690 specific to our problem, IO errors and CSV parsing errors. We're going to
1691 explore two different ways to approach handling these errors.
1693 I'd like to start with `Box<Error>`. Later, we'll see how defining our own
1694 error type can be useful.
1696 ## Error handling with `Box<Error>`
1698 `Box<Error>` is nice because it *just works*. You don't need to define your own
1699 error types and you don't need any `From` implementations. The downside is that
1700 since `Box<Error>` is a trait object, it *erases the type*, which means the
1701 compiler can no longer reason about its underlying type.
1703 [Previously](#the-limits-of-combinators) we started refactoring our code by
1704 changing the type of our function from `T` to `Result<T, OurErrorType>`. In
1705 this case, `OurErrorType` is just `Box<Error>`. But what's `T`? And can we add
1706 a return type to `main`?
1708 The answer to the second question is no, we can't. That means we'll need to
1709 write a new function. But what is `T`? The simplest thing we can do is to
1710 return a list of matching `Row` values as a `Vec<Row>`. (Better code would
1711 return an iterator, but that is left as an exercise to the reader.)
1713 Let's refactor our code into its own function, but keep the calls to `unwrap`.
1714 Note that we opt to handle the possibility of a missing population count by
1715 simply ignoring that row.
1722 struct PopulationCount {
1725 // This is no longer an `Option` because values of this type are only
1726 // constructed if they have a population count.
1730 fn print_usage(program: &str, opts: Options) {
1731 println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
1734 fn search<P: AsRef<Path>>(file_path: P, city: &str) -> Vec<PopulationCount> {
1735 let mut found = vec![];
1736 let file = fs::File::open(file_path).unwrap();
1737 let mut rdr = csv::Reader::from_reader(file);
1738 for row in rdr.decode::<Row>() {
1739 let row = row.unwrap();
1740 match row.population {
1741 None => { } // skip it
1742 Some(count) => if row.city == city {
1743 found.push(PopulationCount {
1745 country: row.country,
1755 let args: Vec<String> = env::args().collect();
1756 let program = args[0].clone();
1758 let mut opts = Options::new();
1759 opts.optflag("h", "help", "Show this usage message.");
1761 let matches = match opts.parse(&args[1..]) {
1763 Err(e) => { panic!(e.to_string()) }
1765 if matches.opt_present("h") {
1766 print_usage(&program, opts);
1770 let data_file = args[1].clone();
1771 let data_path = Path::new(&data_file);
1772 let city = args[2].clone();
1773 for pop in search(&data_path, &city) {
1774 println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1780 While we got rid of one use of `expect` (which is a nicer variant of `unwrap`),
1781 we still should handle the absence of any search results.
1783 To convert this to proper error handling, we need to do the following:
1785 1. Change the return type of `search` to be `Result<Vec<PopulationCount>,
1787 2. Use the [`try!` macro](#code-try-def) so that errors are returned to the
1788 caller instead of panicking the program.
1789 3. Handle the error in `main`.
1794 fn search<P: AsRef<Path>>
1795 (file_path: P, city: &str)
1796 -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
1797 let mut found = vec![];
1798 let file = try!(fs::File::open(file_path));
1799 let mut rdr = csv::Reader::from_reader(file);
1800 for row in rdr.decode::<Row>() {
1801 let row = try!(row);
1802 match row.population {
1803 None => { } // skip it
1804 Some(count) => if row.city == city {
1805 found.push(PopulationCount {
1807 country: row.country,
1813 if found.is_empty() {
1814 Err(From::from("No matching cities with a population were found."))
1821 Instead of `x.unwrap()`, we now have `try!(x)`. Since our function returns a
1822 `Result<T, E>`, the `try!` macro will return early from the function if an
1825 There is one big gotcha in this code: we used `Box<Error + Send + Sync>`
1826 instead of `Box<Error>`. We did this so we could convert a plain string to an
1827 error type. We need these extra bounds so that we can use the
1828 [corresponding `From`
1829 impls](../std/convert/trait.From.html):
1832 // We are making use of this impl in the code above, since we call `From::from`
1833 // on a `&'static str`.
1834 impl<'a, 'b> From<&'b str> for Box<Error + Send + Sync + 'a>
1836 // But this is also useful when you need to allocate a new string for an
1837 // error message, usually with `format!`.
1838 impl From<String> for Box<Error + Send + Sync>
1841 Since `search` now returns a `Result<T, E>`, `main` should use case analysis
1842 when calling `search`:
1846 match search(&data_file, &city) {
1849 println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1852 Err(err) => println!("{}", err)
1857 Now that we've seen how to do proper error handling with `Box<Error>`, let's
1858 try a different approach with our own custom error type. But first, let's take
1859 a quick break from error handling and add support for reading from `stdin`.
1861 ## Reading from stdin
1863 In our program, we accept a single file for input and do one pass over the
1864 data. This means we probably should be able to accept input on stdin. But maybe
1865 we like the current format too—so let's have both!
1867 Adding support for stdin is actually quite easy. There are only three things we
1870 1. Tweak the program arguments so that a single parameter—the
1871 city—can be accepted while the population data is read from stdin.
1872 2. Modify the program so that an option `-f` can take the file, if it
1873 is not passed into stdin.
1874 3. Modify the `search` function to take an *optional* file path. When `None`,
1875 it should know to read from stdin.
1877 First, here's the new usage:
1880 fn print_usage(program: &str, opts: Options) {
1881 println!("{}", opts.usage(&format!("Usage: {} [options] <city>", program)));
1884 The next part is going to be only a little harder:
1888 let mut opts = Options::new();
1889 opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
1890 opts.optflag("h", "help", "Show this usage message.");
1892 let file = matches.opt_str("f");
1893 let data_file = file.as_ref().map(Path::new);
1895 let city = if !matches.free.is_empty() {
1896 matches.free[0].clone()
1898 print_usage(&program, opts);
1902 for pop in search(&data_file, &city) {
1903 println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1908 In this piece of code, we take `file` (which has the type
1909 `Option<String>`), and convert it to a type that `search` can use, in
1910 this case, `&Option<AsRef<Path>>`. To do this, we take a reference of
1911 file, and map `Path::new` onto it. In this case, `as_ref()` converts
1912 the `Option<String>` into an `Option<&str>`, and from there, we can
1913 execute `Path::new` to the content of the optional, and return the
1914 optional of the new value. Once we have that, it is a simple matter of
1915 getting the `city` argument and executing `search`.
1917 Modifying `search` is slightly trickier. The `csv` crate can build a
1919 [any type that implements `io::Read`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.from_reader).
1920 But how can we use the same code over both types? There's actually a
1921 couple ways we could go about this. One way is to write `search` such
1922 that it is generic on some type parameter `R` that satisfies
1923 `io::Read`. Another way is to just use trait objects:
1926 fn search<P: AsRef<Path>>
1927 (file_path: &Option<P>, city: &str)
1928 -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
1929 let mut found = vec![];
1930 let input: Box<io::Read> = match *file_path {
1931 None => Box::new(io::stdin()),
1932 Some(ref file_path) => Box::new(try!(fs::File::open(file_path))),
1934 let mut rdr = csv::Reader::from_reader(input);
1935 // The rest remains unchanged!
1939 ## Error handling with a custom type
1941 Previously, we learned how to
1942 [compose errors using a custom error type](#composing-custom-error-types).
1943 We did this by defining our error type as an `enum` and implementing `Error`
1946 Since we have three distinct errors (IO, CSV parsing and not found), let's
1947 define an `enum` with three variants:
1958 And now for impls on `Display` and `Error`:
1961 impl fmt::Display for CliError {
1962 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1964 CliError::Io(ref err) => err.fmt(f),
1965 CliError::Csv(ref err) => err.fmt(f),
1966 CliError::NotFound => write!(f, "No matching cities with a \
1967 population were found."),
1972 impl Error for CliError {
1973 fn description(&self) -> &str {
1975 CliError::Io(ref err) => err.description(),
1976 CliError::Csv(ref err) => err.description(),
1977 CliError::NotFound => "not found",
1983 Before we can use our `CliError` type in our `search` function, we need to
1984 provide a couple `From` impls. How do we know which impls to provide? Well,
1985 we'll need to convert from both `io::Error` and `csv::Error` to `CliError`.
1986 Those are the only external errors, so we'll only need two `From` impls for
1990 impl From<io::Error> for CliError {
1991 fn from(err: io::Error) -> CliError {
1996 impl From<csv::Error> for CliError {
1997 fn from(err: csv::Error) -> CliError {
2003 The `From` impls are important because of how
2004 [`try!` is defined](#code-try-def). In particular, if an error occurs,
2005 `From::from` is called on the error, which in this case, will convert it to our
2006 own error type `CliError`.
2008 With the `From` impls done, we only need to make two small tweaks to our
2009 `search` function: the return type and the “not found” error. Here it is in
2013 fn search<P: AsRef<Path>>
2014 (file_path: &Option<P>, city: &str)
2015 -> Result<Vec<PopulationCount>, CliError> {
2016 let mut found = vec![];
2017 let input: Box<io::Read> = match *file_path {
2018 None => Box::new(io::stdin()),
2019 Some(ref file_path) => Box::new(try!(fs::File::open(file_path))),
2021 let mut rdr = csv::Reader::from_reader(input);
2022 for row in rdr.decode::<Row>() {
2023 let row = try!(row);
2024 match row.population {
2025 None => { } // skip it
2026 Some(count) => if row.city == city {
2027 found.push(PopulationCount {
2029 country: row.country,
2035 if found.is_empty() {
2036 Err(CliError::NotFound)
2043 No other changes are necessary.
2045 ## Adding functionality
2047 Writing generic code is great, because generalizing stuff is cool, and
2048 it can then be useful later. But sometimes, the juice isn't worth the
2049 squeeze. Look at what we just did in the previous step:
2051 1. Defined a new error type.
2052 2. Added impls for `Error`, `Display` and two for `From`.
2054 The big downside here is that our program didn't improve a whole lot.
2055 There is quite a bit of overhead to representing errors with `enum`s,
2056 especially in short programs like this.
2058 *One* useful aspect of using a custom error type like we've done here is that
2059 the `main` function can now choose to handle errors differently. Previously,
2060 with `Box<Error>`, it didn't have much of a choice: just print the message.
2061 We're still doing that here, but what if we wanted to, say, add a `--quiet`
2062 flag? The `--quiet` flag should silence any verbose output.
2064 Right now, if the program doesn't find a match, it will output a message saying
2065 so. This can be a little clumsy, especially if you intend for the program to
2066 be used in shell scripts.
2068 So let's start by adding the flags. Like before, we need to tweak the usage
2069 string and add a flag to the Option variable. Once we've done that, Getopts does the rest:
2073 let mut opts = Options::new();
2074 opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
2075 opts.optflag("h", "help", "Show this usage message.");
2076 opts.optflag("q", "quiet", "Silences errors and warnings.");
2080 Now we just need to implement our “quiet” functionality. This requires us to
2081 tweak the case analysis in `main`:
2084 match search(&args.arg_data_path, &args.arg_city) {
2085 Err(CliError::NotFound) if args.flag_quiet => process::exit(1),
2086 Err(err) => panic!("{}", err),
2087 Ok(pops) => for pop in pops {
2088 println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
2093 Certainly, we don't want to be quiet if there was an IO error or if the data
2094 failed to parse. Therefore, we use case analysis to check if the error type is
2095 `NotFound` *and* if `--quiet` has been enabled. If the search failed, we still
2096 quit with an exit code (following `grep`'s convention).
2098 If we had stuck with `Box<Error>`, then it would be pretty tricky to implement
2099 the `--quiet` functionality.
2101 This pretty much sums up our case study. From here, you should be ready to go
2102 out into the world and write your own programs and libraries with proper error
2107 Since this chapter is long, it is useful to have a quick summary for error
2108 handling in Rust. These are some good “rules of thumb." They are emphatically
2109 *not* commandments. There are probably good reasons to break every one of these
2112 * If you're writing short example code that would be overburdened by error
2113 handling, it's probably just fine to use `unwrap` (whether that's
2114 [`Result::unwrap`](../std/result/enum.Result.html#method.unwrap),
2115 [`Option::unwrap`](../std/option/enum.Option.html#method.unwrap)
2117 [`Option::expect`](../std/option/enum.Option.html#method.expect)).
2118 Consumers of your code should know to use proper error handling. (If they
2119 don't, send them here!)
2120 * If you're writing a quick 'n' dirty program, don't feel ashamed if you use
2121 `unwrap`. Be warned: if it winds up in someone else's hands, don't be
2122 surprised if they are agitated by poor error messages!
2123 * If you're writing a quick 'n' dirty program and feel ashamed about panicking
2124 anyway, then using either a `String` or a `Box<Error + Send + Sync>` for your
2125 error type (the `Box<Error + Send + Sync>` type is because of the
2126 [available `From` impls](../std/convert/trait.From.html)).
2127 * Otherwise, in a program, define your own error types with appropriate
2128 [`From`](../std/convert/trait.From.html)
2130 [`Error`](../std/error/trait.Error.html)
2131 impls to make the [`try!`](../std/macro.try!.html)
2132 macro more ergonomic.
2133 * If you're writing a library and your code can produce errors, define your own
2134 error type and implement the
2135 [`std::error::Error`](../std/error/trait.Error.html)
2136 trait. Where appropriate, implement
2137 [`From`](../std/convert/trait.From.html) to make both
2138 your library code and the caller's code easier to write. (Because of Rust's
2139 coherence rules, callers will not be able to impl `From` on your error type,
2140 so your library should do it.)
2141 * Learn the combinators defined on
2142 [`Option`](../std/option/enum.Option.html)
2144 [`Result`](../std/result/enum.Result.html).
2145 Using them exclusively can be a bit tiring at times, but I've personally
2146 found a healthy mix of `try!` and combinators to be quite appealing.
2147 `and_then`, `map` and `unwrap_or` are my favorites.
2149 [1]: ../book/patterns.html
2150 [2]: ../std/option/enum.Option.html#method.map
2151 [3]: ../std/option/enum.Option.html#method.unwrap_or
2152 [4]: ../std/option/enum.Option.html#method.unwrap_or_else
2153 [5]: ../std/option/enum.Option.html
2155 [7]: ../std/result/enum.Result.html#method.unwrap
2156 [8]: ../std/fmt/trait.Debug.html
2157 [9]: ../std/primitive.str.html#method.parse
2158 [10]: ../book/associated-types.html
2159 [11]: https://github.com/petewarden/dstkdata
2160 [12]: http://burntsushi.net/stuff/worldcitiespop.csv.gz
2161 [13]: http://burntsushi.net/stuff/uscitiespop.csv.gz
2162 [14]: http://doc.crates.io/guide.html
2163 [15]: http://doc.rust-lang.org/getopts/getopts/index.html