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.
94 let mut argv = env::args();
95 let arg: String = argv.nth(1).unwrap(); // error 1
96 let n: i32 = arg.parse().unwrap(); // error 2
97 println!("{}", 2 * n);
101 If you give this program zero arguments (error 1) or if the first argument
102 isn't an integer (error 2), the program will panic just like in the first
105 You can think of this style of error handling as similar to a bull running
106 through a china shop. The bull will get to where it wants to go, but it will
107 trample everything in the process.
109 ## Unwrapping explained
111 In the previous example, we claimed
112 that the program would simply panic if it reached one of the two error
113 conditions, yet, the program does not include an explicit call to `panic` like
114 the first example. This is because the
115 panic is embedded in the calls to `unwrap`.
117 To “unwrap” something in Rust is to say, “Give me the result of the
118 computation, and if there was an error, just panic and stop the program.”
119 It would be better if we just showed the code for unwrapping because it is so
120 simple, but to do that, we will first need to explore the `Option` and `Result`
121 types. Both of these types have a method called `unwrap` defined on them.
126 [defined in the standard library][1]:
135 The `Option` type is a way to use Rust's type system to express the
136 *possibility of absence*. Encoding the possibility of absence into the type
137 system is an important concept because it will cause the compiler to force the
138 programmer to handle that absence. Let's take a look at an example that tries
139 to find a character in a string:
142 // Searches `haystack` for the Unicode character `needle`. If one is found, the
143 // byte offset of the character is returned. Otherwise, `None` is returned.
144 fn find(haystack: &str, needle: char) -> Option<usize> {
145 for (offset, c) in haystack.char_indices() {
154 Notice that when this function finds a matching character, it doen't just
155 return the `offset`. Instead, it returns `Some(offset)`. `Some` is a variant or
156 a *value constructor* for the `Option` type. You can think of it as a function
157 with the type `fn<T>(value: T) -> Option<T>`. Correspondingly, `None` is also a
158 value constructor, except it has no arguments. You can think of `None` as a
159 function with the type `fn<T>() -> Option<T>`.
161 This might seem like much ado about nothing, but this is only half of the
162 story. The other half is *using* the `find` function we've written. Let's try
163 to use it to find the extension in a file name.
166 # fn find(_: &str, _: char) -> Option<usize> { None }
168 let file_name = "foobar.rs";
169 match find(file_name, '.') {
170 None => println!("No file extension found."),
171 Some(i) => println!("File extension: {}", &file_name[i+1..]),
176 This code uses [pattern matching][1] to do *case
177 analysis* on the `Option<usize>` returned by the `find` function. In fact, case
178 analysis is the only way to get at the value stored inside an `Option<T>`. This
179 means that you, as the programmer, must handle the case when an `Option<T>` is
180 `None` instead of `Some(t)`.
182 But wait, what about `unwrap` used in [`unwrap-double`](#code-unwrap-double)?
183 There was no case analysis there! Instead, the case analysis was put inside the
184 `unwrap` method for you. You could define it yourself if you want:
193 fn unwrap(self) -> T {
195 Option::Some(val) => val,
197 panic!("called `Option::unwrap()` on a `None` value"),
203 The `unwrap` method *abstracts away the case analysis*. This is precisely the thing
204 that makes `unwrap` ergonomic to use. Unfortunately, that `panic!` means that
205 `unwrap` is not composable: it is the bull in the china shop.
207 ### Composing `Option<T>` values
209 In [`option-ex-string-find`](#code-option-ex-string-find-2)
210 we saw how to use `find` to discover the extension in a file name. Of course,
211 not all file names have a `.` in them, so it's possible that the file name has
212 no extension. This *possibility of absence* is encoded into the types using
213 `Option<T>`. In other words, the compiler will force us to address the
214 possibility that an extension does not exist. In our case, we just print out a
215 message saying as such.
217 Getting the extension of a file name is a pretty common operation, so it makes
218 sense to put it into a function:
221 # fn find(_: &str, _: char) -> Option<usize> { None }
222 // Returns the extension of the given file name, where the extension is defined
223 // as all characters proceding the first `.`.
224 // If `file_name` has no `.`, then `None` is returned.
225 fn extension_explicit(file_name: &str) -> Option<&str> {
226 match find(file_name, '.') {
228 Some(i) => Some(&file_name[i+1..]),
233 (Pro-tip: don't use this code. Use the
234 [`extension`](../std/path/struct.Path.html#method.extension)
235 method in the standard library instead.)
237 The code stays simple, but the important thing to notice is that the type of
238 `find` forces us to consider the possibility of absence. This is a good thing
239 because it means the compiler won't let us accidentally forget about the case
240 where a file name doesn't have an extension. On the other hand, doing explicit
241 case analysis like we've done in `extension_explicit` every time can get a bit
244 In fact, the case analysis in `extension_explicit` follows a very common
245 pattern: *map* a function on to the value inside of an `Option<T>`, unless the
246 option is `None`, in which case, just return `None`.
248 Rust has parametric polymorphism, so it is very easy to define a combinator
249 that abstracts this pattern:
252 fn map<F, T, A>(option: Option<T>, f: F) -> Option<A> where F: FnOnce(T) -> A {
255 Some(value) => Some(f(value)),
260 Indeed, `map` is [defined as a method][2] on `Option<T>` in the standard library.
262 Armed with our new combinator, we can rewrite our `extension_explicit` method
263 to get rid of the case analysis:
266 # fn find(_: &str, _: char) -> Option<usize> { None }
267 // Returns the extension of the given file name, where the extension is defined
268 // as all characters proceding the first `.`.
269 // If `file_name` has no `.`, then `None` is returned.
270 fn extension(file_name: &str) -> Option<&str> {
271 find(file_name, '.').map(|i| &file_name[i+1..])
275 One other pattern that we find is very common is assigning a default value to
276 the case when an `Option` value is `None`. For example, maybe your program
277 assumes that the extension of a file is `rs` even if none is present. As you
278 might imagine, the case analysis for this is not specific to file
279 extensions - it can work with any `Option<T>`:
282 fn unwrap_or<T>(option: Option<T>, default: T) -> T {
285 Some(value) => value,
290 The trick here is that the default value must have the same type as the value
291 that might be inside the `Option<T>`. Using it is dead simple in our case:
294 # fn find(haystack: &str, needle: char) -> Option<usize> {
295 # for (offset, c) in haystack.char_indices() {
297 # return Some(offset);
303 # fn extension(file_name: &str) -> Option<&str> {
304 # find(file_name, '.').map(|i| &file_name[i+1..])
307 assert_eq!(extension("foobar.csv").unwrap_or("rs"), "csv");
308 assert_eq!(extension("foobar").unwrap_or("rs"), "rs");
312 (Note that `unwrap_or` is [defined as a method][3] on `Option<T>` in the
313 standard library, so we use that here instead of the free-standing function we
314 defined above. Don't forget to check out the more general [`unwrap_or_else`][4]
317 There is one more combinator that we think is worth paying special attention to:
318 `and_then`. It makes it easy to compose distinct computations that admit the
319 *possibility of absence*. For example, much of the code in this section is
320 about finding an extension given a file name. In order to do this, you first
321 need the file name which is typically extracted from a file *path*. While most
322 file paths have a file name, not *all* of them do. For example, `.`, `..` or
325 So, we are tasked with the challenge of finding an extension given a file
326 *path*. Let's start with explicit case analysis:
329 # fn extension(file_name: &str) -> Option<&str> { None }
330 fn file_path_ext_explicit(file_path: &str) -> Option<&str> {
331 match file_name(file_path) {
333 Some(name) => match extension(name) {
335 Some(ext) => Some(ext),
340 fn file_name(file_path: &str) -> Option<&str> {
341 // implementation elided
346 You might think that we could just use the `map` combinator to reduce the case
347 analysis, but its type doesn't quite fit. Namely, `map` takes a function that
348 does something only with the inner value. The result of that function is then
349 *always* [rewrapped with `Some`](#code-option-map). Instead, we need something
350 like `map`, but which allows the caller to return another `Option`. Its generic
351 implementation is even simpler than `map`:
354 fn and_then<F, T, A>(option: Option<T>, f: F) -> Option<A>
355 where F: FnOnce(T) -> Option<A> {
358 Some(value) => f(value),
363 Now we can rewrite our `file_path_ext` function without explicit case analysis:
366 # fn extension(file_name: &str) -> Option<&str> { None }
367 # fn file_name(file_path: &str) -> Option<&str> { None }
368 fn file_path_ext(file_path: &str) -> Option<&str> {
369 file_name(file_path).and_then(extension)
373 The `Option` type has many other combinators [defined in the standard
374 library][5]. It is a good idea to skim this list and familiarize
375 yourself with what's available—they can often reduce case analysis
376 for you. Familiarizing yourself with these combinators will pay
377 dividends because many of them are also defined (with similar
378 semantics) for `Result`, which we will talk about next.
380 Combinators make using types like `Option` ergonomic because they reduce
381 explicit case analysis. They are also composable because they permit the caller
382 to handle the possibility of absence in their own way. Methods like `unwrap`
383 remove choices because they will panic if `Option<T>` is `None`.
387 The `Result` type is also
388 [defined in the standard library][6]:
397 The `Result` type is a richer version of `Option`. Instead of expressing the
398 possibility of *absence* like `Option` does, `Result` expresses the possibility
399 of *error*. Usually, the *error* is used to explain why the result of some
400 computation failed. This is a strictly more general form of `Option`. Consider
401 the following type alias, which is semantically equivalent to the real
402 `Option<T>` in every way:
405 type Option<T> = Result<T, ()>;
408 This fixes the second type parameter of `Result` to always be `()` (pronounced
409 “unit” or “empty tuple”). Exactly one value inhabits the `()` type: `()`. (Yup,
410 the type and value level terms have the same notation!)
412 The `Result` type is a way of representing one of two possible outcomes in a
413 computation. By convention, one outcome is meant to be expected or “`Ok`” while
414 the other outcome is meant to be unexpected or “`Err`”.
416 Just like `Option`, the `Result` type also has an
419 in the standard library. Let's define it:
422 # enum Result<T, E> { Ok(T), Err(E) }
423 impl<T, E: ::std::fmt::Debug> Result<T, E> {
424 fn unwrap(self) -> T {
426 Result::Ok(val) => val,
428 panic!("called `Result::unwrap()` on an `Err` value: {:?}", err),
434 This is effectively the same as our [definition for
435 `Option::unwrap`](#code-option-def-unwrap), except it includes the
436 error value in the `panic!` message. This makes debugging easier, but
437 it also requires us to add a [`Debug`][8] constraint on the `E` type
438 parameter (which represents our error type). Since the vast majority
439 of types should satisfy the `Debug` constraint, this tends to work out
440 in practice. (`Debug` on a type simply means that there's a reasonable
441 way to print a human readable description of values with that type.)
443 OK, let's move on to an example.
447 The Rust standard library makes converting strings to integers dead simple.
448 It's so easy in fact, that it is very tempting to write something like the
452 fn double_number(number_str: &str) -> i32 {
453 2 * number_str.parse::<i32>().unwrap()
457 let n: i32 = double_number("10");
462 At this point, you should be skeptical of calling `unwrap`. For example, if
463 the string doesn't parse as a number, you'll get a panic:
466 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
469 This is rather unsightly, and if this happened inside a library you're
470 using, you might be understandably annoyed. Instead, we should try to
471 handle the error in our function and let the caller decide what to
472 do. This means changing the return type of `double_number`. But to
473 what? Well, that requires looking at the signature of the [`parse`
474 method][9] in the standard library:
478 fn parse<F: FromStr>(&self) -> Result<F, F::Err>;
482 Hmm. So we at least know that we need to use a `Result`. Certainly, it's
483 possible that this could have returned an `Option`. After all, a string either
484 parses as a number or it doesn't, right? That's certainly a reasonable way to
485 go, but the implementation internally distinguishes *why* the string didn't
486 parse as an integer. (Whether it's an empty string, an invalid digit, too big
487 or too small.) Therefore, using a `Result` makes sense because we want to
488 provide more information than simply “absence.” We want to say *why* the
489 parsing failed. You should try to emulate this line of reasoning when faced
490 with a choice between `Option` and `Result`. If you can provide detailed error
491 information, then you probably should. (We'll see more on this later.)
493 OK, but how do we write our return type? The `parse` method as defined
494 above is generic over all the different number types defined in the
495 standard library. We could (and probably should) also make our
496 function generic, but let's favor explicitness for the moment. We only
497 care about `i32`, so we need to [find its implementation of
498 `FromStr`](../std/primitive.i32.html) (do a `CTRL-F` in your browser
499 for “FromStr”) and look at its [associated type][10] `Err`. We did
500 this so we can find the concrete error type. In this case, it's
501 [`std::num::ParseIntError`](../std/num/struct.ParseIntError.html).
502 Finally, we can rewrite our function:
505 use std::num::ParseIntError;
507 fn double_number(number_str: &str) -> Result<i32, ParseIntError> {
508 match number_str.parse::<i32>() {
510 Err(err) => Err(err),
515 match double_number("10") {
516 Ok(n) => assert_eq!(n, 20),
517 Err(err) => println!("Error: {:?}", err),
522 This is a little better, but now we've written a lot more code! The case
523 analysis has once again bitten us.
525 Combinators to the rescue! Just like `Option`, `Result` has lots of combinators
526 defined as methods. There is a large intersection of common combinators between
527 `Result` and `Option`. In particular, `map` is part of that intersection:
530 use std::num::ParseIntError;
532 fn double_number(number_str: &str) -> Result<i32, ParseIntError> {
533 number_str.parse::<i32>().map(|n| 2 * n)
537 match double_number("10") {
538 Ok(n) => assert_eq!(n, 20),
539 Err(err) => println!("Error: {:?}", err),
544 The usual suspects are all there for `Result`, including
545 [`unwrap_or`](../std/result/enum.Result.html#method.unwrap_or) and
546 [`and_then`](../std/result/enum.Result.html#method.and_then).
547 Additionally, since `Result` has a second type parameter, there are
548 combinators that affect only the error type, such as
549 [`map_err`](../std/result/enum.Result.html#method.map_err) (instead of
550 `map`) and [`or_else`](../std/result/enum.Result.html#method.or_else)
551 (instead of `and_then`).
553 ### The `Result` type alias idiom
555 In the standard library, you may frequently see types like
556 `Result<i32>`. But wait, [we defined `Result`](#code-result-def-1) to
557 have two type parameters. How can we get away with only specifying
558 one? The key is to define a `Result` type alias that *fixes* one of
559 the type parameters to a particular type. Usually the fixed type is
560 the error type. For example, our previous example parsing integers
561 could be rewritten like this:
564 use std::num::ParseIntError;
567 type Result<T> = result::Result<T, ParseIntError>;
569 fn double_number(number_str: &str) -> Result<i32> {
574 Why would we do this? Well, if we have a lot of functions that could return
575 `ParseIntError`, then it's much more convenient to define an alias that always
576 uses `ParseIntError` so that we don't have to write it out all the time.
578 The most prominent place this idiom is used in the standard library is
579 with [`io::Result`](../std/io/type.Result.html). Typically, one writes
580 `io::Result<T>`, which makes it clear that you're using the `io`
581 module's type alias instead of the plain definition from
582 `std::result`. (This idiom is also used for
583 [`fmt::Result`](../std/fmt/type.Result.html).)
585 ## A brief interlude: unwrapping isn't evil
587 If you've been following along, you might have noticed that I've taken a pretty
588 hard line against calling methods like `unwrap` that could `panic` and abort
589 your program. *Generally speaking*, this is good advice.
591 However, `unwrap` can still be used judiciously. What exactly justifies use of
592 `unwrap` is somewhat of a grey area and reasonable people can disagree. I'll
593 summarize some of my *opinions* on the matter.
595 * **In examples and quick 'n' dirty code.** Sometimes you're writing examples
596 or a quick program, and error handling simply isn't important. Beating the
597 convenience of `unwrap` can be hard in such scenarios, so it is very
599 * **When panicking indicates a bug in the program.** When the invariants of
600 your code should prevent a certain case from happening (like, say, popping
601 from an empty stack), then panicking can be permissible. This is because it
602 exposes a bug in your program. This can be explicit, like from an `assert!`
603 failing, or it could be because your index into an array was out of bounds.
605 This is probably not an exhaustive list. Moreover, when using an
606 `Option`, it is often better to use its
607 [`expect`](../std/option/enum.Option.html#method.expect)
608 method. `expect` does exactly the same thing as `unwrap`, except it
609 prints a message you give to `expect`. This makes the resulting panic
610 a bit nicer to deal with, since it will show your message instead of
611 “called unwrap on a `None` value.”
613 My advice boils down to this: use good judgment. There's a reason why the words
614 “never do X” or “Y is considered harmful” don't appear in my writing. There are
615 trade offs to all things, and it is up to you as the programmer to determine
616 what is acceptable for your use cases. My goal is only to help you evaluate
617 trade offs as accurately as possible.
619 Now that we've covered the basics of error handling in Rust, and
620 explained unwrapping, let's start exploring more of the standard
623 # Working with multiple error types
625 Thus far, we've looked at error handling where everything was either an
626 `Option<T>` or a `Result<T, SomeError>`. But what happens when you have both an
627 `Option` and a `Result`? Or what if you have a `Result<T, Error1>` and a
628 `Result<T, Error2>`? Handling *composition of distinct error types* is the next
629 challenge in front of us, and it will be the major theme throughout the rest of
632 ## Composing `Option` and `Result`
634 So far, I've talked about combinators defined for `Option` and combinators
635 defined for `Result`. We can use these combinators to compose results of
636 different computations without doing explicit case analysis.
638 Of course, in real code, things aren't always as clean. Sometimes you have a
639 mix of `Option` and `Result` types. Must we resort to explicit case analysis,
640 or can we continue using combinators?
642 For now, let's revisit one of the first examples in this chapter:
648 let mut argv = env::args();
649 let arg: String = argv.nth(1).unwrap(); // error 1
650 let n: i32 = arg.parse().unwrap(); // error 2
651 println!("{}", 2 * n);
655 Given our new found knowledge of `Option`, `Result` and their various
656 combinators, we should try to rewrite this so that errors are handled properly
657 and the program doesn't panic if there's an error.
659 The tricky aspect here is that `argv.nth(1)` produces an `Option` while
660 `arg.parse()` produces a `Result`. These aren't directly composable. When faced
661 with both an `Option` and a `Result`, the solution is *usually* to convert the
662 `Option` to a `Result`. In our case, the absence of a command line parameter
663 (from `env::args()`) means the user didn't invoke the program correctly. We
664 could just use a `String` to describe the error. Let's try:
669 fn double_arg(mut argv: env::Args) -> Result<i32, String> {
671 .ok_or("Please give at least one argument".to_owned())
672 .and_then(|arg| arg.parse::<i32>().map_err(|err| err.to_string()))
676 match double_arg(env::args()) {
677 Ok(n) => println!("{}", n),
678 Err(err) => println!("Error: {}", err),
683 There are a couple new things in this example. The first is the use of the
684 [`Option::ok_or`](../std/option/enum.Option.html#method.ok_or)
685 combinator. This is one way to convert an `Option` into a `Result`. The
686 conversion requires you to specify what error to use if `Option` is `None`.
687 Like the other combinators we've seen, its definition is very simple:
690 fn ok_or<T, E>(option: Option<T>, err: E) -> Result<T, E> {
692 Some(val) => Ok(val),
698 The other new combinator used here is
699 [`Result::map_err`](../std/result/enum.Result.html#method.map_err).
700 This is just like `Result::map`, except it maps a function on to the *error*
701 portion of a `Result` value. If the `Result` is an `Ok(...)` value, then it is
704 We use `map_err` here because it is necessary for the error types to remain
705 the same (because of our use of `and_then`). Since we chose to convert the
706 `Option<String>` (from `argv.nth(1)`) to a `Result<String, String>`, we must
707 also convert the `ParseIntError` from `arg.parse()` to a `String`.
709 ## The limits of combinators
711 Doing IO and parsing input is a very common task, and it's one that I
712 personally have done a lot of in Rust. Therefore, we will use (and continue to
713 use) IO and various parsing routines to exemplify error handling.
715 Let's start simple. We are tasked with opening a file, reading all of its
716 contents and converting its contents to a number. Then we multiply it by `2`
717 and print the output.
719 Although I've tried to convince you not to use `unwrap`, it can be useful
720 to first write your code using `unwrap`. It allows you to focus on your problem
721 instead of the error handling, and it exposes the points where proper error
722 handling need to occur. Let's start there so we can get a handle on the code,
723 and then refactor it to use better error handling.
730 fn file_double<P: AsRef<Path>>(file_path: P) -> i32 {
731 let mut file = File::open(file_path).unwrap(); // error 1
732 let mut contents = String::new();
733 file.read_to_string(&mut contents).unwrap(); // error 2
734 let n: i32 = contents.trim().parse().unwrap(); // error 3
739 let doubled = file_double("foobar");
740 println!("{}", doubled);
744 (N.B. The `AsRef<Path>` is used because those are the
746 `std::fs::File::open`](../std/fs/struct.File.html#method.open).
747 This makes it ergnomic to use any kind of string as a file path.)
749 There are three different errors that can occur here:
751 1. A problem opening the file.
752 2. A problem reading data from the file.
753 3. A problem parsing the data as a number.
755 The first two problems are described via the
756 [`std::io::Error`](../std/io/struct.Error.html) type. We know this
757 because of the return types of
758 [`std::fs::File::open`](../std/fs/struct.File.html#method.open) and
759 [`std::io::Read::read_to_string`](../std/io/trait.Read.html#method.read_to_string).
760 (Note that they both use the [`Result` type alias
761 idiom](#the-result-type-alias-idiom) described previously. If you
762 click on the `Result` type, you'll [see the type
763 alias](../std/io/type.Result.html), and consequently, the underlying
764 `io::Error` type.) The third problem is described by the
765 [`std::num::ParseIntError`](../std/num/struct.ParseIntError.html)
766 type. The `io::Error` type in particular is *pervasive* throughout the
767 standard library. You will see it again and again.
769 Let's start the process of refactoring the `file_double` function. To make this
770 function composable with other components of the program, it should *not* panic
771 if any of the above error conditions are met. Effectively, this means that the
772 function should *return an error* if any of its operations fail. Our problem is
773 that the return type of `file_double` is `i32`, which does not give us any
774 useful way of reporting an error. Thus, we must start by changing the return
775 type from `i32` to something else.
777 The first thing we need to decide: should we use `Option` or `Result`? We
778 certainly could use `Option` very easily. If any of the three errors occur, we
779 could simply return `None`. This will work *and it is better than panicking*,
780 but we can do a lot better. Instead, we should pass some detail about the error
781 that occurred. Since we want to express the *possibility of error*, we should
782 use `Result<i32, E>`. But what should `E` be? Since two *different* types of
783 errors can occur, we need to convert them to a common type. One such type is
784 `String`. Let's see how that impacts our code:
791 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
792 File::open(file_path)
793 .map_err(|err| err.to_string())
794 .and_then(|mut file| {
795 let mut contents = String::new();
796 file.read_to_string(&mut contents)
797 .map_err(|err| err.to_string())
800 .and_then(|contents| {
801 contents.trim().parse::<i32>()
802 .map_err(|err| err.to_string())
808 match file_double("foobar") {
809 Ok(n) => println!("{}", n),
810 Err(err) => println!("Error: {}", err),
815 This code looks a bit hairy. It can take quite a bit of practice before code
816 like this becomes easy to write. The way we write it is by *following the
817 types*. As soon as we changed the return type of `file_double` to
818 `Result<i32, String>`, we had to start looking for the right combinators. In
819 this case, we only used three different combinators: `and_then`, `map` and
822 `and_then` is used to chain multiple computations where each computation could
823 return an error. After opening the file, there are two more computations that
824 could fail: reading from the file and parsing the contents as a number.
825 Correspondingly, there are two calls to `and_then`.
827 `map` is used to apply a function to the `Ok(...)` value of a `Result`. For
828 example, the very last call to `map` multiplies the `Ok(...)` value (which is
829 an `i32`) by `2`. If an error had occurred before that point, this operation
830 would have been skipped because of how `map` is defined.
832 `map_err` is the trick the makes all of this work. `map_err` is just like
833 `map`, except it applies a function to the `Err(...)` value of a `Result`. In
834 this case, we want to convert all of our errors to one type: `String`. Since
835 both `io::Error` and `num::ParseIntError` implement `ToString`, we can call the
836 `to_string()` method to convert them.
838 With all of that said, the code is still hairy. Mastering use of combinators is
839 important, but they have their limits. Let's try a different approach: early
844 I'd like to take the code from the previous section and rewrite it using *early
845 returns*. Early returns let you exit the function early. We can't return early
846 in `file_double` from inside another closure, so we'll need to revert back to
847 explicit case analysis.
854 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
855 let mut file = match File::open(file_path) {
857 Err(err) => return Err(err.to_string()),
859 let mut contents = String::new();
860 if let Err(err) = file.read_to_string(&mut contents) {
861 return Err(err.to_string());
863 let n: i32 = match contents.trim().parse() {
865 Err(err) => return Err(err.to_string()),
871 match file_double("foobar") {
872 Ok(n) => println!("{}", n),
873 Err(err) => println!("Error: {}", err),
878 Reasonable people can disagree over whether this code is better that the code
879 that uses combinators, but if you aren't familiar with the combinator approach,
880 this code looks simpler to read to me. It uses explicit case analysis with
881 `match` and `if let`. If an error occurs, it simply stops executing the
882 function and returns the error (by converting it to a string).
884 Isn't this a step backwards though? Previously, we said that the key to
885 ergonomic error handling is reducing explicit case analysis, yet we've reverted
886 back to explicit case analysis here. It turns out, there are *multiple* ways to
887 reduce explicit case analysis. Combinators aren't the only way.
891 A cornerstone of error handling in Rust is the `try!` macro. The `try!` macro
892 abstracts case analysis just like combinators, but unlike combinators, it also
893 abstracts *control flow*. Namely, it can abstract the *early return* pattern
896 Here is a simplified definition of a `try!` macro:
900 ($e:expr) => (match $e {
902 Err(err) => return Err(err),
907 (The [real definition](../std/macro.try!.html) is a bit more
908 sophisticated. We will address that later.)
910 Using the `try!` macro makes it very easy to simplify our last example. Since
911 it does the case analysis and the early return for us, we get tighter code that
919 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
920 let mut file = try!(File::open(file_path).map_err(|e| e.to_string()));
921 let mut contents = String::new();
922 try!(file.read_to_string(&mut contents).map_err(|e| e.to_string()));
923 let n = try!(contents.trim().parse::<i32>().map_err(|e| e.to_string()));
928 match file_double("foobar") {
929 Ok(n) => println!("{}", n),
930 Err(err) => println!("Error: {}", err),
935 The `map_err` calls are still necessary given
936 [our definition of `try!`](#code-try-def-simple).
937 This is because the error types still need to be converted to `String`.
938 The good news is that we will soon learn how to remove those `map_err` calls!
939 The bad news is that we will need to learn a bit more about a couple important
940 traits in the standard library before we can remove the `map_err` calls.
942 ## Defining your own error type
944 Before we dive into some of the standard library error traits, I'd like to wrap
945 up this section by removing the use of `String` as our error type in the
948 Using `String` as we did in our previous examples is convenient because it's
949 easy to convert errors to strings, or even make up your own errors as strings
950 on the spot. However, using `String` for your errors has some downsides.
952 The first downside is that the error messages tend to clutter your
953 code. It's possible to define the error messages elsewhere, but unless
954 you're unusually disciplined, it is very tempting to embed the error
955 message into your code. Indeed, we did exactly this in a [previous
956 example](#code-error-double-string).
958 The second and more important downside is that `String`s are *lossy*. That is,
959 if all errors are converted to strings, then the errors we pass to the caller
960 become completely opaque. The only reasonable thing the caller can do with a
961 `String` error is show it to the user. Certainly, inspecting the string to
962 determine the type of error is not robust. (Admittedly, this downside is far
963 more important inside of a library as opposed to, say, an application.)
965 For example, the `io::Error` type embeds an
966 [`io::ErrorKind`](../std/io/enum.ErrorKind.html),
967 which is *structured data* that represents what went wrong during an IO
968 operation. This is important because you might want to react differently
969 depending on the error. (e.g., A `BrokenPipe` error might mean quitting your
970 program gracefully while a `NotFound` error might mean exiting with an error
971 code and showing an error to the user.) With `io::ErrorKind`, the caller can
972 examine the type of an error with case analysis, which is strictly superior
973 to trying to tease out the details of an error inside of a `String`.
975 Instead of using a `String` as an error type in our previous example of reading
976 an integer from a file, we can define our own error type that represents errors
977 with *structured data*. We endeavor to not drop information from underlying
978 errors in case the caller wants to inspect the details.
980 The ideal way to represent *one of many possibilities* is to define our own
981 sum type using `enum`. In our case, an error is either an `io::Error` or a
982 `num::ParseIntError`, so a natural definition arises:
988 // We derive `Debug` because all types should probably derive `Debug`.
989 // This gives us a reasonable human readable description of `CliError` values.
993 Parse(num::ParseIntError),
997 Tweaking our code is very easy. Instead of converting errors to strings, we
998 simply convert them to our `CliError` type using the corresponding value
1003 # enum CliError { Io(::std::io::Error), Parse(::std::num::ParseIntError) }
1006 use std::path::Path;
1008 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
1009 let mut file = try!(File::open(file_path).map_err(CliError::Io));
1010 let mut contents = String::new();
1011 try!(file.read_to_string(&mut contents).map_err(CliError::Io));
1012 let n: i32 = try!(contents.trim().parse().map_err(CliError::Parse));
1017 match file_double("foobar") {
1018 Ok(n) => println!("{}", n),
1019 Err(err) => println!("Error: {:?}", err),
1024 The only change here is switching `map_err(|e| e.to_string())` (which converts
1025 errors to strings) to `map_err(CliError::Io)` or `map_err(CliError::Parse)`.
1026 The *caller* gets to decide the level of detail to report to the user. In
1027 effect, using a `String` as an error type removes choices from the caller while
1028 using a custom `enum` error type like `CliError` gives the caller all of the
1029 conveniences as before in addition to *structured data* describing the error.
1031 A rule of thumb is to define your own error type, but a `String` error type
1032 will do in a pinch, particularly if you're writing an application. If you're
1033 writing a library, defining your own error type should be strongly preferred so
1034 that you don't remove choices from the caller unnecessarily.
1036 # Standard library traits used for error handling
1038 The standard library defines two integral traits for error handling:
1039 [`std::error::Error`](../std/error/trait.Error.html) and
1040 [`std::convert::From`](../std/convert/trait.From.html). While `Error`
1041 is designed specifically for generically describing errors, the `From`
1042 trait serves a more general role for converting values between two
1045 ## The `Error` trait
1047 The `Error` trait is [defined in the standard
1048 library](../std/error/trait.Error.html):
1051 use std::fmt::{Debug, Display};
1053 trait Error: Debug + Display {
1054 /// A short description of the error.
1055 fn description(&self) -> &str;
1057 /// The lower level cause of this error, if any.
1058 fn cause(&self) -> Option<&Error> { None }
1062 This trait is super generic because it is meant to be implemented for *all*
1063 types that represent errors. This will prove useful for writing composable code
1064 as we'll see later. Otherwise, the trait allows you to do at least the
1067 * Obtain a `Debug` representation of the error.
1068 * Obtain a user-facing `Display` representation of the error.
1069 * Obtain a short description of the error (via the `description` method).
1070 * Inspect the causal chain of an error, if one exists (via the `cause` method).
1072 The first two are a result of `Error` requiring impls for both `Debug` and
1073 `Display`. The latter two are from the two methods defined on `Error`. The
1074 power of `Error` comes from the fact that all error types impl `Error`, which
1075 means errors can be existentially quantified as a
1076 [trait object](../book/trait-objects.html).
1077 This manifests as either `Box<Error>` or `&Error`. Indeed, the `cause` method
1078 returns an `&Error`, which is itself a trait object. We'll revisit the
1079 `Error` trait's utility as a trait object later.
1081 For now, it suffices to show an example implementing the `Error` trait. Let's
1082 use the error type we defined in the
1083 [previous section](#defining-your-own-error-type):
1089 // We derive `Debug` because all types should probably derive `Debug`.
1090 // This gives us a reasonable human readable description of `CliError` values.
1094 Parse(num::ParseIntError),
1098 This particular error type represents the possibility of two types of errors
1099 occurring: an error dealing with I/O or an error converting a string to a
1100 number. The error could represent as many error types as you want by adding new
1101 variants to the `enum` definition.
1103 Implementing `Error` is pretty straight-forward. It's mostly going to be a lot
1104 explicit case analysis.
1110 impl fmt::Display for CliError {
1111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1113 // Both underlying errors already impl `Display`, so we defer to
1114 // their implementations.
1115 CliError::Io(ref err) => write!(f, "IO error: {}", err),
1116 CliError::Parse(ref err) => write!(f, "Parse error: {}", err),
1121 impl error::Error for CliError {
1122 fn description(&self) -> &str {
1123 // Both underlying errors already impl `Error`, so we defer to their
1126 CliError::Io(ref err) => err.description(),
1127 // Normally we can just write `err.description()`, but the error
1128 // type has a concrete method called `description`, which conflicts
1129 // with the trait method. For now, we must explicitly call
1130 // `description` through the `Error` trait.
1131 CliError::Parse(ref err) => error::Error::description(err),
1135 fn cause(&self) -> Option<&error::Error> {
1137 // N.B. Both of these implicitly cast `err` from their concrete
1138 // types (either `&io::Error` or `&num::ParseIntError`)
1139 // to a trait object `&Error`. This works because both error types
1140 // implement `Error`.
1141 CliError::Io(ref err) => Some(err),
1142 CliError::Parse(ref err) => Some(err),
1148 We note that this is a very typical implementation of `Error`: match on your
1149 different error types and satisfy the contracts defined for `description` and
1154 The `std::convert::From` trait is
1155 [defined in the standard
1156 library](../std/convert/trait.From.html):
1164 Deliciously simple, yes? `From` is very useful because it gives us a generic
1165 way to talk about conversion *from* a particular type `T` to some other type
1166 (in this case, “some other type” is the subject of the impl, or `Self`).
1167 The crux of `From` is the
1168 [set of implementations provided by the standard
1169 library](../std/convert/trait.From.html).
1171 Here are a few simple examples demonstrating how `From` works:
1174 let string: String = From::from("foo");
1175 let bytes: Vec<u8> = From::from("foo");
1176 let cow: ::std::borrow::Cow<str> = From::from("foo");
1179 OK, so `From` is useful for converting between strings. But what about errors?
1180 It turns out, there is one critical impl:
1183 impl<'a, E: Error + 'a> From<E> for Box<Error + 'a>
1186 This impl says that for *any* type that impls `Error`, we can convert it to a
1187 trait object `Box<Error>`. This may not seem terribly surprising, but it is
1188 useful in a generic context.
1190 Remember the two errors we were dealing with previously? Specifically,
1191 `io::Error` and `num::ParseIntError`. Since both impl `Error`, they work with
1195 use std::error::Error;
1200 // We have to jump through some hoops to actually get error values.
1201 let io_err: io::Error = io::Error::last_os_error();
1202 let parse_err: num::ParseIntError = "not a number".parse::<i32>().unwrap_err();
1204 // OK, here are the conversions.
1205 let err1: Box<Error> = From::from(io_err);
1206 let err2: Box<Error> = From::from(parse_err);
1209 There is a really important pattern to recognize here. Both `err1` and `err2`
1210 have the *same type*. This is because they are existentially quantified types,
1211 or trait objects. In particularly, their underlying type is *erased* from the
1212 compiler's knowledge, so it truly sees `err1` and `err2` as exactly the same.
1213 Additionally, we constructed `err1` and `err2` using precisely the same
1214 function call: `From::from`. This is because `From::from` is overloaded on both
1215 its argument and its return type.
1217 This pattern is important because it solves a problem we had earlier: it gives
1218 us a way to reliably convert errors to the same type using the same function.
1220 Time to revisit an old friend; the `try!` macro.
1222 ## The real `try!` macro
1224 Previously, we presented this definition of `try!`:
1228 ($e:expr) => (match $e {
1230 Err(err) => return Err(err),
1235 This is not it's real definition. It's real definition is
1236 [in the standard library](../std/macro.try!.html):
1240 ($e:expr) => (match $e {
1242 Err(err) => return Err(::std::convert::From::from(err)),
1247 There's one tiny but powerful change: the error value is passed through
1248 `From::from`. This makes the `try!` macro a lot more powerful because it gives
1249 you automatic type conversion for free.
1251 Armed with our more powerful `try!` macro, let's take a look at code we wrote
1252 previously to read a file and convert its contents to an integer:
1257 use std::path::Path;
1259 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
1260 let mut file = try!(File::open(file_path).map_err(|e| e.to_string()));
1261 let mut contents = String::new();
1262 try!(file.read_to_string(&mut contents).map_err(|e| e.to_string()));
1263 let n = try!(contents.trim().parse::<i32>().map_err(|e| e.to_string()));
1268 Earlier, we promised that we could get rid of the `map_err` calls. Indeed, all
1269 we have to do is pick a type that `From` works with. As we saw in the previous
1270 section, `From` has an impl that let's it convert any error type into a
1274 use std::error::Error;
1277 use std::path::Path;
1279 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, Box<Error>> {
1280 let mut file = try!(File::open(file_path));
1281 let mut contents = String::new();
1282 try!(file.read_to_string(&mut contents));
1283 let n = try!(contents.trim().parse::<i32>());
1288 We are getting very close to ideal error handling. Our code has very little
1289 overhead as a result from error handling because the `try!` macro encapsulates
1290 three things simultaneously:
1294 3. Error type conversion.
1296 When all three things are combined, we get code that is unencumbered by
1297 combinators, calls to `unwrap` or case analysis.
1299 There's one little nit left: the `Box<Error>` type is *opaque*. If we
1300 return a `Box<Error>` to the caller, the caller can't (easily) inspect
1301 underlying error type. The situation is certainly better than `String`
1302 because the caller can call methods like
1303 [`description`](../std/error/trait.Error.html#tymethod.description)
1304 and [`cause`](../std/error/trait.Error.html#method.cause), but the
1305 limitation remains: `Box<Error>` is opaque. (N.B. This isn't entirely
1306 true because Rust does have runtime reflection, which is useful in
1307 some scenarios that are [beyond the scope of this
1308 chapter](https://crates.io/crates/error).)
1310 It's time to revisit our custom `CliError` type and tie everything together.
1312 ## Composing custom error types
1314 In the last section, we looked at the real `try!` macro and how it does
1315 automatic type conversion for us by calling `From::from` on the error value.
1316 In particular, we converted errors to `Box<Error>`, which works, but the type
1317 is opaque to callers.
1319 To fix this, we use the same remedy that we're already familiar with: a custom
1320 error type. Once again, here is the code that reads the contents of a file and
1321 converts it to an integer:
1325 use std::io::{self, Read};
1327 use std::path::Path;
1329 // We derive `Debug` because all types should probably derive `Debug`.
1330 // This gives us a reasonable human readable description of `CliError` values.
1334 Parse(num::ParseIntError),
1337 fn file_double_verbose<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
1338 let mut file = try!(File::open(file_path).map_err(CliError::Io));
1339 let mut contents = String::new();
1340 try!(file.read_to_string(&mut contents).map_err(CliError::Io));
1341 let n: i32 = try!(contents.trim().parse().map_err(CliError::Parse));
1346 Notice that we still have the calls to `map_err`. Why? Well, recall the
1347 definitions of [`try!`](#code-try-def) and [`From`](#code-from-def). The
1348 problem is that there is no `From` impl that allows us to convert from error
1349 types like `io::Error` and `num::ParseIntError` to our own custom `CliError`.
1350 Of course, it is easy to fix this! Since we defined `CliError`, we can impl
1355 # enum CliError { Io(io::Error), Parse(num::ParseIntError) }
1359 impl From<io::Error> for CliError {
1360 fn from(err: io::Error) -> CliError {
1365 impl From<num::ParseIntError> for CliError {
1366 fn from(err: num::ParseIntError) -> CliError {
1367 CliError::Parse(err)
1372 All these impls are doing is teaching `From` how to create a `CliError` from
1373 other error types. In our case, construction is as simple as invoking the
1374 corresponding value constructor. Indeed, it is *typically* this easy.
1376 We can finally rewrite `file_double`:
1381 # enum CliError { Io(::std::io::Error), Parse(::std::num::ParseIntError) }
1382 # impl From<io::Error> for CliError {
1383 # fn from(err: io::Error) -> CliError { CliError::Io(err) }
1385 # impl From<num::ParseIntError> for CliError {
1386 # fn from(err: num::ParseIntError) -> CliError { CliError::Parse(err) }
1391 use std::path::Path;
1393 fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
1394 let mut file = try!(File::open(file_path));
1395 let mut contents = String::new();
1396 try!(file.read_to_string(&mut contents));
1397 let n: i32 = try!(contents.trim().parse());
1402 The only thing we did here was remove the calls to `map_err`. They are no
1403 longer needed because the `try!` macro invokes `From::from` on the error value.
1404 This works because we've provided `From` impls for all the error types that
1407 If we modified our `file_double` function to perform some other operation, say,
1408 convert a string to a float, then we'd need to add a new variant to our error
1417 ParseInt(num::ParseIntError),
1418 ParseFloat(num::ParseFloatError),
1422 And add a new `From` impl:
1426 # Io(::std::io::Error),
1427 # ParseInt(num::ParseIntError),
1428 # ParseFloat(num::ParseFloatError),
1433 impl From<num::ParseFloatError> for CliError {
1434 fn from(err: num::ParseFloatError) -> CliError {
1435 CliError::ParseFloat(err)
1442 ## Advice for library writers
1444 If your library needs to report custom errors, then you should
1445 probably define your own error type. It's up to you whether or not to
1446 expose its representation (like
1447 [`ErrorKind`](../std/io/enum.ErrorKind.html)) or keep it hidden (like
1448 [`ParseIntError`](../std/num/struct.ParseIntError.html)). Regardless
1449 of how you do it, it's usually good practice to at least provide some
1450 information about the error beyond just its `String`
1451 representation. But certainly, this will vary depending on use cases.
1453 At a minimum, you should probably implement the
1454 [`Error`](../std/error/trait.Error.html)
1455 trait. This will give users of your library some minimum flexibility for
1456 [composing errors](#the-real-try-macro). Implementing the `Error` trait also
1457 means that users are guaranteed the ability to obtain a string representation
1458 of an error (because it requires impls for both `fmt::Debug` and
1461 Beyond that, it can also be useful to provide implementations of `From` on your
1462 error types. This allows you (the library author) and your users to
1463 [compose more detailed errors](#composing-custom-error-types). For example,
1464 [`csv::Error`](http://burntsushi.net/rustdoc/csv/enum.Error.html)
1465 provides `From` impls for both `io::Error` and `byteorder::Error`.
1467 Finally, depending on your tastes, you may also want to define a
1468 [`Result` type alias](#the-result-type-alias-idiom), particularly if your
1469 library defines a single error type. This is used in the standard library
1470 for [`io::Result`](../std/io/type.Result.html)
1471 and [`fmt::Result`](../std/fmt/type.Result.html).
1473 # Case study: A program to read population data
1475 This chapter was long, and depending on your background, it might be
1476 rather dense. While there is plenty of example code to go along with
1477 the prose, most of it was specifically designed to be pedagogical. So,
1478 we're going to do something new: a case study.
1480 For this, we're going to build up a command line program that lets you
1481 query world population data. The objective is simple: you give it a location
1482 and it will tell you the population. Despite the simplicity, there is a lot
1485 The data we'll be using comes from the [Data Science
1486 Toolkit][11]. I've prepared some data from it for this exercise. You
1487 can either grab the [world population data][12] (41MB gzip compressed,
1488 145MB uncompressed) or just the [US population data][13] (2.2MB gzip
1489 compressed, 7.2MB uncompressed).
1491 Up until now, we've kept the code limited to Rust's standard library. For a real
1492 task like this though, we'll want to at least use something to parse CSV data,
1493 parse the program arguments and decode that stuff into Rust types automatically. For that, we'll use the
1494 [`csv`](https://crates.io/crates/csv),
1495 and [`rustc-serialize`](https://crates.io/crates/rustc-serialize) crates.
1499 We're not going to spend a lot of time on setting up a project with
1500 Cargo because it is already covered well in [the Cargo
1501 chapter](../book/hello-cargo) and [Cargo's documentation][14].
1503 To get started from scratch, run `cargo new --bin city-pop` and make sure your
1504 `Cargo.toml` looks something like this:
1510 authors = ["Andrew Gallant <jamslam@gmail.com>"]
1517 rustc-serialize = "0.*"
1521 You should already be able to run:
1524 cargo build --release
1525 ./target/release/city-pop
1526 # Outputs: Hello, world!
1531 Let's get argument parsing out of the way. we won't go into too much
1532 detail on Getopts, but there is [some good documentation][15]
1533 describing it. The short story is that Getopts generates an argument
1534 parser and a help message from a vector of options (The fact that it
1535 is a vector is hidden behind a struct and a set of methods). Once the
1536 parsing is done, we can decode the program arguments into a Rust
1537 struct. From there, we can get information about the flags, for
1538 instance, wether they were passed in, and what arguments they
1539 had. Here's our program with the appropriate `extern crate`
1540 statements, and the basic argument setup for Getopts:
1543 extern crate getopts;
1544 extern crate rustc_serialize;
1546 use getopts::Options;
1549 fn print_usage(program: &str, opts: Options) {
1550 println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
1554 let args: Vec<String> = env::args().collect();
1555 let program = args[0].clone();
1557 let mut opts = Options::new();
1558 opts.optflag("h", "help", "Show this usage message.");
1560 let matches = match opts.parse(&args[1..]) {
1562 Err(e) => { panic!(e.to_string()) }
1564 if matches.opt_present("h") {
1565 print_usage(&program, opts);
1568 let data_path = args[1].clone();
1569 let city = args[2].clone();
1571 // Do stuff with information
1575 First, we get a vector of the arguments passed into our program. We
1576 then store the first one, knowing that it is our program's name. Once
1577 that's done, we set up our argument flags, in this case a simplistic
1578 help message flag. Once we have the argument flags set up, we use
1579 `Options.parse` to parse the argument vector (starting from index one,
1580 becouse index 0 is the program name). If this was successful, we
1581 assign matches to the parsed object, if not, we panic. Once past that,
1582 we test if the user passed in the help flag, and if so print the usage
1583 message. The option help messages are constructed by Getopts, so all
1584 we have to do to print the usage message is tell it what we want it to
1585 print for the program name and template. If the user has not passed in
1586 the help flag, we assign the proper variables to their corresponding
1589 ## Writing the logic
1591 We're all different in how we write code, but error handling is
1592 usually the last thing we want to think about. This isn't very good
1593 practice for good design, but it can be useful for rapidly
1594 prototyping. In our case, because Rust forces us to be explicit about
1595 error handling, it will also make it obvious what parts of our program
1596 can cause errors. Why? Because Rust will make us call `unwrap`! This
1597 can give us a nice bird's eye view of how we need to approach error
1600 In this case study, the logic is really simple. All we need to do is parse the
1601 CSV data given to us and print out a field in matching rows. Let's do it. (Make
1602 sure to add `extern crate csv;` to the top of your file.)
1605 // This struct represents the data in each row of the CSV file.
1606 // Type based decoding absolves us of a lot of the nitty gritty error
1607 // handling, like parsing strings as integers or floats.
1608 #[derive(Debug, RustcDecodable)]
1612 accent_city: String,
1615 // Not every row has data for the population, latitude or longitude!
1616 // So we express them as `Option` types, which admits the possibility of
1617 // absence. The CSV parser will fill in the correct value for us.
1618 population: Option<u64>,
1619 latitude: Option<f64>,
1620 longitude: Option<f64>,
1623 fn print_usage(program: &str, opts: Options) {
1624 println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
1628 let args: Vec<String> = env::args().collect();
1629 let program = args[0].clone();
1631 let mut opts = Options::new();
1632 opts.optflag("h", "help", "Show this usage message.");
1634 let matches = match opts.parse(&args[1..]) {
1636 Err(e) => { panic!(e.to_string()) }
1639 if matches.opt_present("h") {
1640 print_usage(&program, opts);
1644 let data_file = args[1].clone();
1645 let data_path = Path::new(&data_file);
1646 let city = args[2].clone();
1648 let file = fs::File::open(data_path).unwrap();
1649 let mut rdr = csv::Reader::from_reader(file);
1651 for row in rdr.decode::<Row>() {
1652 let row = row.unwrap();
1654 if row.city == city {
1655 println!("{}, {}: {:?}",
1656 row.city, row.country,
1657 row.population.expect("population count"));
1663 Let's outline the errors. We can start with the obvious: the three places that
1666 1. [`fs::File::open`](../std/fs/struct.File.html#method.open)
1668 [`io::Error`](../std/io/struct.Error.html).
1669 2. [`csv::Reader::decode`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.decode)
1670 decodes one record at a time, and
1672 record](http://burntsushi.net/rustdoc/csv/struct.DecodedRecords.html)
1673 (look at the `Item` associated type on the `Iterator` impl)
1675 [`csv::Error`](http://burntsushi.net/rustdoc/csv/enum.Error.html).
1676 3. If `row.population` is `None`, then calling `expect` will panic.
1678 Are there any others? What if we can't find a matching city? Tools like `grep`
1679 will return an error code, so we probably should too. So we have logic errors
1680 specific to our problem, IO errors and CSV parsing errors. We're going to
1681 explore two different ways to approach handling these errors.
1683 I'd like to start with `Box<Error>`. Later, we'll see how defining our own
1684 error type can be useful.
1686 ## Error handling with `Box<Error>`
1688 `Box<Error>` is nice because it *just works*. You don't need to define your own
1689 error types and you don't need any `From` implementations. The downside is that
1690 since `Box<Error>` is a trait object, it *erases the type*, which means the
1691 compiler can no longer reason about its underlying type.
1693 [Previously](#the-limits-of-combinators) we started refactoring our code by
1694 changing the type of our function from `T` to `Result<T, OurErrorType>`. In
1695 this case, `OurErrorType` is just `Box<Error>`. But what's `T`? And can we add
1696 a return type to `main`?
1698 The answer to the second question is no, we can't. That means we'll need to
1699 write a new function. But what is `T`? The simplest thing we can do is to
1700 return a list of matching `Row` values as a `Vec<Row>`. (Better code would
1701 return an iterator, but that is left as an exercise to the reader.)
1703 Let's refactor our code into its own function, but keep the calls to `unwrap`.
1704 Note that we opt to handle the possibility of a missing population count by
1705 simply ignoring that row.
1712 struct PopulationCount {
1715 // This is no longer an `Option` because values of this type are only
1716 // constructed if they have a population count.
1720 fn print_usage(program: &str, opts: Options) {
1721 println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
1724 fn search<P: AsRef<Path>>(file_path: P, city: &str) -> Vec<PopulationCount> {
1725 let mut found = vec![];
1726 let file = fs::File::open(file_path).unwrap();
1727 let mut rdr = csv::Reader::from_reader(file);
1728 for row in rdr.decode::<Row>() {
1729 let row = row.unwrap();
1730 match row.population {
1731 None => { } // skip it
1732 Some(count) => if row.city == city {
1733 found.push(PopulationCount {
1735 country: row.country,
1745 let args: Vec<String> = env::args().collect();
1746 let program = args[0].clone();
1748 let mut opts = Options::new();
1749 opts.optflag("h", "help", "Show this usage message.");
1751 let matches = match opts.parse(&args[1..]) {
1753 Err(e) => { panic!(e.to_string()) }
1755 if matches.opt_present("h") {
1756 print_usage(&program, opts);
1760 let data_file = args[1].clone();
1761 let data_path = Path::new(&data_file);
1762 let city = args[2].clone();
1763 for pop in search(&data_path, &city) {
1764 println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1770 While we got rid of one use of `expect` (which is a nicer variant of `unwrap`),
1771 we still should handle the absence of any search results.
1773 To convert this to proper error handling, we need to do the following:
1775 1. Change the return type of `search` to be `Result<Vec<PopulationCount>,
1777 2. Use the [`try!` macro](#code-try-def) so that errors are returned to the
1778 caller instead of panicking the program.
1779 3. Handle the error in `main`.
1784 fn search<P: AsRef<Path>>
1785 (file_path: P, city: &str)
1786 -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
1787 let mut found = vec![];
1788 let file = try!(fs::File::open(file_path));
1789 let mut rdr = csv::Reader::from_reader(file);
1790 for row in rdr.decode::<Row>() {
1791 let row = try!(row);
1792 match row.population {
1793 None => { } // skip it
1794 Some(count) => if row.city == city {
1795 found.push(PopulationCount {
1797 country: row.country,
1803 if found.is_empty() {
1804 Err(From::from("No matching cities with a population were found."))
1811 Instead of `x.unwrap()`, we now have `try!(x)`. Since our function returns a
1812 `Result<T, E>`, the `try!` macro will return early from the function if an
1815 There is one big gotcha in this code: we used `Box<Error + Send + Sync>`
1816 instead of `Box<Error>`. We did this so we could convert a plain string to an
1817 error type. We need these extra bounds so that we can use the
1818 [corresponding `From`
1819 impls](../std/convert/trait.From.html):
1822 // We are making use of this impl in the code above, since we call `From::from`
1823 // on a `&'static str`.
1824 impl<'a, 'b> From<&'b str> for Box<Error + Send + Sync + 'a>
1826 // But this is also useful when you need to allocate a new string for an
1827 // error message, usually with `format!`.
1828 impl From<String> for Box<Error + Send + Sync>
1831 Now that we've seen how to do proper error handling with `Box<Error>`, let's
1832 try a different approach with our own custom error type. But first, let's take
1833 a quick break from error handling and add support for reading from `stdin`.
1835 ## Reading from stdin
1837 In our program, we accept a single file for input and do one pass over the
1838 data. This means we probably should be able to accept input on stdin. But maybe
1839 we like the current format too—so let's have both!
1841 Adding support for stdin is actually quite easy. There are only two things we
1844 1. Tweak the program arguments so that a single parameter—the
1845 city—can be accepted while the population data is read from stdin.
1846 2. Modify the program so that an option `-f` can take the file, if it
1847 is not passed into stdin.
1848 3. Modify the `search` function to take an *optional* file path. When `None`,
1849 it should know to read from stdin.
1851 First, here's the new usage:
1854 fn print_usage(program: &str, opts: Options) {
1855 println!("{}", opts.usage(&format!("Usage: {} [options] <city>", program)));
1858 The next part is going to be only a little harder:
1862 let mut opts = Options::new();
1863 opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
1864 opts.optflag("h", "help", "Show this usage message.");
1866 let file = matches.opt_str("f");
1867 let data_file = file.as_ref().map(Path::new);
1869 let city = if !matches.free.is_empty() {
1870 matches.free[0].clone()
1872 print_usage(&program, opts);
1876 for pop in search(&data_file, &city) {
1877 println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
1882 In this peice of code, we take `file` (which has the type
1883 `Option<String>`), and convert it to a type that `search` can use, in
1884 this case, `&Option<AsRef<Path>>`. Do do this, we take a reference of
1885 file, and map `Path::new` onto it. In this case, `as_ref()` converts
1886 the `Option<String>` into an `Option<&str>`, and from there, we can
1887 execute `Path::new` to the content of the optional, and return the
1888 optional of the new value. Once we have that, it is a simple matter of
1889 getting the `city` argument and executing `search`.
1891 Modifying `search` is slightly trickier. The `csv` crate can build a
1893 [any type that implements `io::Read`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.from_reader).
1894 But how can we use the same code over both types? There's actually a
1895 couple ways we could go about this. One way is to write `search` such
1896 that it is generic on some type parameter `R` that satisfies
1897 `io::Read`. Another way is to just use trait objects:
1900 fn search<P: AsRef<Path>>
1901 (file_path: &Option<P>, city: &str)
1902 -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
1903 let mut found = vec![];
1904 let input: Box<io::Read> = match *file_path {
1905 None => Box::new(io::stdin()),
1906 Some(ref file_path) => Box::new(try!(fs::File::open(file_path))),
1908 let mut rdr = csv::Reader::from_reader(input);
1909 // The rest remains unchanged!
1913 ## Error handling with a custom type
1915 Previously, we learned how to
1916 [compose errors using a custom error type](#composing-custom-error-types).
1917 We did this by defining our error type as an `enum` and implementing `Error`
1920 Since we have three distinct errors (IO, CSV parsing and not found), let's
1921 define an `enum` with three variants:
1932 And now for impls on `Display` and `Error`:
1935 impl fmt::Display for CliError {
1936 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1938 CliError::Io(ref err) => err.fmt(f),
1939 CliError::Csv(ref err) => err.fmt(f),
1940 CliError::NotFound => write!(f, "No matching cities with a \
1941 population were found."),
1946 impl Error for CliError {
1947 fn description(&self) -> &str {
1949 CliError::Io(ref err) => err.description(),
1950 CliError::Csv(ref err) => err.description(),
1951 CliError::NotFound => "not found",
1957 Before we can use our `CliError` type in our `search` function, we need to
1958 provide a couple `From` impls. How do we know which impls to provide? Well,
1959 we'll need to convert from both `io::Error` and `csv::Error` to `CliError`.
1960 Those are the only external errors, so we'll only need two `From` impls for
1964 impl From<io::Error> for CliError {
1965 fn from(err: io::Error) -> CliError {
1970 impl From<csv::Error> for CliError {
1971 fn from(err: csv::Error) -> CliError {
1977 The `From` impls are important because of how
1978 [`try!` is defined](#code-try-def). In particular, if an error occurs,
1979 `From::from` is called on the error, which in this case, will convert it to our
1980 own error type `CliError`.
1982 With the `From` impls done, we only need to make two small tweaks to our
1983 `search` function: the return type and the “not found” error. Here it is in
1987 fn search<P: AsRef<Path>>
1988 (file_path: &Option<P>, city: &str)
1989 -> Result<Vec<PopulationCount>, CliError> {
1990 let mut found = vec![];
1991 let input: Box<io::Read> = match *file_path {
1992 None => Box::new(io::stdin()),
1993 Some(ref file_path) => Box::new(try!(fs::File::open(file_path))),
1995 let mut rdr = csv::Reader::from_reader(input);
1996 for row in rdr.decode::<Row>() {
1997 let row = try!(row);
1998 match row.population {
1999 None => { } // skip it
2000 Some(count) => if row.city == city {
2001 found.push(PopulationCount {
2003 country: row.country,
2009 if found.is_empty() {
2010 Err(CliError::NotFound)
2017 No other changes are necessary.
2019 ## Adding functionality
2021 Writing generic code is great, because generalizing stuff is cool, and
2022 it can then be useful later. But sometimes, the juice isn't worth the
2023 squeeze. Look at what we just did in the previous step:
2025 1. Defined a new error type.
2026 2. Added impls for `Error`, `Display` and two for `From`.
2028 The big downside here is that our program didn't improve a whole lot.
2029 There is quite a bit of overhead to representing errors with `enum`s,
2030 especially in short programs like this.
2032 *One* useful aspect of using a custom error type like we've done here is that
2033 the `main` function can now choose to handle errors differently. Previously,
2034 with `Box<Error>`, it didn't have much of a choice: just print the message.
2035 We're still doing that here, but what if we wanted to, say, add a `--quiet`
2036 flag? The `--quiet` flag should silence any verbose output.
2038 Right now, if the program doesn't find a match, it will output a message saying
2039 so. This can be a little clumsy, especially if you intend for the program to
2040 be used in shell scripts.
2042 So let's start by adding the flags. Like before, we need to tweak the usage
2043 string and add a flag to the Option variable. Once were done that, Getopts does the rest:
2047 let mut opts = Options::new();
2048 opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
2049 opts.optflag("h", "help", "Show this usage message.");
2050 opts.optflag("q", "quit", "Silences errors and warnings.");
2054 Now we just need to implement our “quiet” functionality. This requires us to
2055 tweak the case analysis in `main`:
2058 match search(&args.arg_data_path, &args.arg_city) {
2059 Err(CliError::NotFound) if args.flag_quiet => process::exit(1),
2060 Err(err) => fatal!("{}", err),
2061 Ok(pops) => for pop in pops {
2062 println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
2067 Certainly, we don't want to be quiet if there was an IO error or if the data
2068 failed to parse. Therefore, we use case analysis to check if the error type is
2069 `NotFound` *and* if `--quiet` has been enabled. If the search failed, we still
2070 quit with an exit code (following `grep`'s convention).
2072 If we had stuck with `Box<Error>`, then it would be pretty tricky to implement
2073 the `--quiet` functionality.
2075 This pretty much sums up our case study. From here, you should be ready to go
2076 out into the world and write your own programs and libraries with proper error
2081 Since this chapter is long, it is useful to have a quick summary for error
2082 handling in Rust. These are some good “rules of thumb." They are emphatically
2083 *not* commandments. There are probably good reasons to break every one of these
2086 * If you're writing short example code that would be overburdened by error
2087 handling, it's probably just fine to use `unwrap` (whether that's
2088 [`Result::unwrap`](../std/result/enum.Result.html#method.unwrap),
2089 [`Option::unwrap`](../std/option/enum.Option.html#method.unwrap)
2091 [`Option::expect`](../std/option/enum.Option.html#method.expect)).
2092 Consumers of your code should know to use proper error handling. (If they
2093 don't, send them here!)
2094 * If you're writing a quick 'n' dirty program, don't feel ashamed if you use
2095 `unwrap`. Be warned: if it winds up in someone else's hands, don't be
2096 surprised if they are agitated by poor error messages!
2097 * If you're writing a quick 'n' dirty program and feel ashamed about panicking
2098 anyway, then using either a `String` or a `Box<Error + Send + Sync>` for your
2099 error type (the `Box<Error + Send + Sync>` type is because of the
2100 [available `From` impls](../std/convert/trait.From.html)).
2101 * Otherwise, in a program, define your own error types with appropriate
2102 [`From`](../std/convert/trait.From.html)
2104 [`Error`](../std/error/trait.Error.html)
2105 impls to make the [`try!`](../std/macro.try!.html)
2106 macro more ergnomic.
2107 * If you're writing a library and your code can produce errors, define your own
2108 error type and implement the
2109 [`std::error::Error`](../std/error/trait.Error.html)
2110 trait. Where appropriate, implement
2111 [`From`](../std/convert/trait.From.html) to make both
2112 your library code and the caller's code easier to write. (Because of Rust's
2113 coherence rules, callers will not be able to impl `From` on your error type,
2114 so your library should do it.)
2115 * Learn the combinators defined on
2116 [`Option`](../std/option/enum.Option.html)
2118 [`Result`](../std/result/enum.Result.html).
2119 Using them exclusively can be a bit tiring at times, but I've personally
2120 found a healthy mix of `try!` and combinators to be quite appealing.
2121 `and_then`, `map` and `unwrap_or` are my favorites.
2123 [1]: ../book/patterns.html
2124 [2]: ../std/option/enum.Option.html#method.map
2125 [3]: ../std/option/enum.Option.html#method.unwrap_or
2126 [4]: ../std/option/enum.Option.html#method.unwrap_or_else
2127 [5]: ../std/option/enum.Option.html
2129 [7]: ../std/result/enum.Result.html#method.unwrap
2130 [8]: ../std/fmt/trait.Debug.html
2131 [9]: ../std/primitive.str.html#method.parse
2132 [10]: ../book/associated-types.html
2133 [11]: https://github.com/petewarden/dstkdata
2134 [12]: http://burntsushi.net/stuff/worldcitiespop.csv.gz
2135 [13]: http://burntsushi.net/stuff/uscitiespop.csv.gz
2136 [14]: http://doc.crates.io/guide.html
2137 [15]: http://doc.rust-lang.org/getopts/getopts/index.html