PageRenderTime 36ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/src/doc/book/error-handling.md

https://github.com/paulstansifer/rust
Markdown | 2163 lines | 1786 code | 377 blank | 0 comment | 0 complexity | 1175aad2549245e8b7b82b6a1e2b6aed MD5 | raw file
Possible License(s): 0BSD, Apache-2.0, MIT, AGPL-1.0
  1. % Error Handling
  2. Like most programming languages, Rust encourages the programmer to handle
  3. errors in a particular way. Generally speaking, error handling is divided into
  4. two broad categories: exceptions and return values. Rust opts for return
  5. values.
  6. In this chapter, we intend to provide a comprehensive treatment of how to deal
  7. with errors in Rust. More than that, we will attempt to introduce error handling
  8. one piece at a time so that you'll come away with a solid working knowledge of
  9. how everything fits together.
  10. When done naïvely, error handling in Rust can be verbose and annoying. This
  11. chapter will explore those stumbling blocks and demonstrate how to use the
  12. standard library to make error handling concise and ergonomic.
  13. # Table of Contents
  14. This chapter is very long, mostly because we start at the very beginning with
  15. sum types and combinators, and try to motivate the way Rust does error handling
  16. incrementally. As such, programmers with experience in other expressive type
  17. systems may want to jump around.
  18. * [The Basics](#the-basics)
  19. * [Unwrapping explained](#unwrapping-explained)
  20. * [The `Option` type](#the-option-type)
  21. * [Composing `Option<T>` values](#composing-optiont-values)
  22. * [The `Result` type](#the-result-type)
  23. * [Parsing integers](#parsing-integers)
  24. * [The `Result` type alias idiom](#the-result-type-alias-idiom)
  25. * [A brief interlude: unwrapping isn't evil](#a-brief-interlude-unwrapping-isnt-evil)
  26. * [Working with multiple error types](#working-with-multiple-error-types)
  27. * [Composing `Option` and `Result`](#composing-option-and-result)
  28. * [The limits of combinators](#the-limits-of-combinators)
  29. * [Early returns](#early-returns)
  30. * [The `try!` macro](#the-try-macro)
  31. * [Defining your own error type](#defining-your-own-error-type)
  32. * [Standard library traits used for error handling](#standard-library-traits-used-for-error-handling)
  33. * [The `Error` trait](#the-error-trait)
  34. * [The `From` trait](#the-from-trait)
  35. * [The real `try!` macro](#the-real-try-macro)
  36. * [Composing custom error types](#composing-custom-error-types)
  37. * [Advice for library writers](#advice-for-library-writers)
  38. * [Case study: A program to read population data](#case-study-a-program-to-read-population-data)
  39. * [Initial setup](#initial-setup)
  40. * [Argument parsing](#argument-parsing)
  41. * [Writing the logic](#writing-the-logic)
  42. * [Error handling with `Box<Error>`](#error-handling-with-boxerror)
  43. * [Reading from stdin](#reading-from-stdin)
  44. * [Error handling with a custom type](#error-handling-with-a-custom-type)
  45. * [Adding functionality](#adding-functionality)
  46. * [The short story](#the-short-story)
  47. # The Basics
  48. You can think of error handling as using *case analysis* to determine whether
  49. a computation was successful or not. As you will see, the key to ergonomic error
  50. handling is reducing the amount of explicit case analysis the programmer has to
  51. do while keeping code composable.
  52. Keeping code composable is important, because without that requirement, we
  53. could [`panic`](../std/macro.panic!.html) whenever we
  54. come across something unexpected. (`panic` causes the current task to unwind,
  55. and in most cases, the entire program aborts.) Here's an example:
  56. ```rust,should_panic
  57. // Guess a number between 1 and 10.
  58. // If it matches the number we had in mind, return true. Else, return false.
  59. fn guess(n: i32) -> bool {
  60. if n < 1 || n > 10 {
  61. panic!("Invalid number: {}", n);
  62. }
  63. n == 5
  64. }
  65. fn main() {
  66. guess(11);
  67. }
  68. ```
  69. If you try running this code, the program will crash with a message like this:
  70. ```text
  71. thread '<main>' panicked at 'Invalid number: 11', src/bin/panic-simple.rs:5
  72. ```
  73. Here's another example that is slightly less contrived. A program that accepts
  74. an integer as an argument, doubles it and prints it.
  75. <span id="code-unwrap-double"></span>
  76. ```rust,should_panic
  77. use std::env;
  78. fn main() {
  79. let mut argv = env::args();
  80. let arg: String = argv.nth(1).unwrap(); // error 1
  81. let n: i32 = arg.parse().unwrap(); // error 2
  82. println!("{}", 2 * n);
  83. }
  84. ```
  85. If you give this program zero arguments (error 1) or if the first argument
  86. isn't an integer (error 2), the program will panic just like in the first
  87. example.
  88. You can think of this style of error handling as similar to a bull running
  89. through a china shop. The bull will get to where it wants to go, but it will
  90. trample everything in the process.
  91. ## Unwrapping explained
  92. In the previous example, we claimed
  93. that the program would simply panic if it reached one of the two error
  94. conditions, yet, the program does not include an explicit call to `panic` like
  95. the first example. This is because the
  96. panic is embedded in the calls to `unwrap`.
  97. To unwrap something in Rust is to say, Give me the result of the
  98. computation, and if there was an error, just panic and stop the program.
  99. It would be better if we just showed the code for unwrapping because it is so
  100. simple, but to do that, we will first need to explore the `Option` and `Result`
  101. types. Both of these types have a method called `unwrap` defined on them.
  102. ### The `Option` type
  103. The `Option` type is [defined in the standard library][5]:
  104. ```rust
  105. enum Option<T> {
  106. None,
  107. Some(T),
  108. }
  109. ```
  110. The `Option` type is a way to use Rust's type system to express the
  111. *possibility of absence*. Encoding the possibility of absence into the type
  112. system is an important concept because it will cause the compiler to force the
  113. programmer to handle that absence. Let's take a look at an example that tries
  114. to find a character in a string:
  115. <span id="code-option-ex-string-find"></span>
  116. ```rust
  117. // Searches `haystack` for the Unicode character `needle`. If one is found, the
  118. // byte offset of the character is returned. Otherwise, `None` is returned.
  119. fn find(haystack: &str, needle: char) -> Option<usize> {
  120. for (offset, c) in haystack.char_indices() {
  121. if c == needle {
  122. return Some(offset);
  123. }
  124. }
  125. None
  126. }
  127. ```
  128. Notice that when this function finds a matching character, it doesn't just
  129. return the `offset`. Instead, it returns `Some(offset)`. `Some` is a variant or
  130. a *value constructor* for the `Option` type. You can think of it as a function
  131. with the type `fn<T>(value: T) -> Option<T>`. Correspondingly, `None` is also a
  132. value constructor, except it has no arguments. You can think of `None` as a
  133. function with the type `fn<T>() -> Option<T>`.
  134. This might seem like much ado about nothing, but this is only half of the
  135. story. The other half is *using* the `find` function we've written. Let's try
  136. to use it to find the extension in a file name.
  137. ```rust
  138. # fn find(_: &str, _: char) -> Option<usize> { None }
  139. fn main() {
  140. let file_name = "foobar.rs";
  141. match find(file_name, '.') {
  142. None => println!("No file extension found."),
  143. Some(i) => println!("File extension: {}", &file_name[i+1..]),
  144. }
  145. }
  146. ```
  147. This code uses [pattern matching][1] to do *case
  148. analysis* on the `Option<usize>` returned by the `find` function. In fact, case
  149. analysis is the only way to get at the value stored inside an `Option<T>`. This
  150. means that you, as the programmer, must handle the case when an `Option<T>` is
  151. `None` instead of `Some(t)`.
  152. But wait, what about `unwrap`,which we used [`previously`](#code-unwrap-double)?
  153. There was no case analysis there! Instead, the case analysis was put inside the
  154. `unwrap` method for you. You could define it yourself if you want:
  155. <span id="code-option-def-unwrap"></span>
  156. ```rust
  157. enum Option<T> {
  158. None,
  159. Some(T),
  160. }
  161. impl<T> Option<T> {
  162. fn unwrap(self) -> T {
  163. match self {
  164. Option::Some(val) => val,
  165. Option::None =>
  166. panic!("called `Option::unwrap()` on a `None` value"),
  167. }
  168. }
  169. }
  170. ```
  171. The `unwrap` method *abstracts away the case analysis*. This is precisely the thing
  172. that makes `unwrap` ergonomic to use. Unfortunately, that `panic!` means that
  173. `unwrap` is not composable: it is the bull in the china shop.
  174. ### Composing `Option<T>` values
  175. In an [example from before](#code-option-ex-string-find),
  176. we saw how to use `find` to discover the extension in a file name. Of course,
  177. not all file names have a `.` in them, so it's possible that the file name has
  178. no extension. This *possibility of absence* is encoded into the types using
  179. `Option<T>`. In other words, the compiler will force us to address the
  180. possibility that an extension does not exist. In our case, we just print out a
  181. message saying as such.
  182. Getting the extension of a file name is a pretty common operation, so it makes
  183. sense to put it into a function:
  184. ```rust
  185. # fn find(_: &str, _: char) -> Option<usize> { None }
  186. // Returns the extension of the given file name, where the extension is defined
  187. // as all characters proceeding the first `.`.
  188. // If `file_name` has no `.`, then `None` is returned.
  189. fn extension_explicit(file_name: &str) -> Option<&str> {
  190. match find(file_name, '.') {
  191. None => None,
  192. Some(i) => Some(&file_name[i+1..]),
  193. }
  194. }
  195. ```
  196. (Pro-tip: don't use this code. Use the
  197. [`extension`](../std/path/struct.Path.html#method.extension)
  198. method in the standard library instead.)
  199. The code stays simple, but the important thing to notice is that the type of
  200. `find` forces us to consider the possibility of absence. This is a good thing
  201. because it means the compiler won't let us accidentally forget about the case
  202. where a file name doesn't have an extension. On the other hand, doing explicit
  203. case analysis like we've done in `extension_explicit` every time can get a bit
  204. tiresome.
  205. In fact, the case analysis in `extension_explicit` follows a very common
  206. pattern: *map* a function on to the value inside of an `Option<T>`, unless the
  207. option is `None`, in which case, just return `None`.
  208. Rust has parametric polymorphism, so it is very easy to define a combinator
  209. that abstracts this pattern:
  210. <span id="code-option-map"></span>
  211. ```rust
  212. fn map<F, T, A>(option: Option<T>, f: F) -> Option<A> where F: FnOnce(T) -> A {
  213. match option {
  214. None => None,
  215. Some(value) => Some(f(value)),
  216. }
  217. }
  218. ```
  219. Indeed, `map` is [defined as a method][2] on `Option<T>` in the standard library.
  220. Armed with our new combinator, we can rewrite our `extension_explicit` method
  221. to get rid of the case analysis:
  222. ```rust
  223. # fn find(_: &str, _: char) -> Option<usize> { None }
  224. // Returns the extension of the given file name, where the extension is defined
  225. // as all characters proceeding the first `.`.
  226. // If `file_name` has no `.`, then `None` is returned.
  227. fn extension(file_name: &str) -> Option<&str> {
  228. find(file_name, '.').map(|i| &file_name[i+1..])
  229. }
  230. ```
  231. One other pattern we commonly find is assigning a default value to the case
  232. when an `Option` value is `None`. For example, maybe your program assumes that
  233. the extension of a file is `rs` even if none is present. As you might imagine,
  234. the case analysis for this is not specific to file extensions - it can work
  235. with any `Option<T>`:
  236. ```rust
  237. fn unwrap_or<T>(option: Option<T>, default: T) -> T {
  238. match option {
  239. None => default,
  240. Some(value) => value,
  241. }
  242. }
  243. ```
  244. The trick here is that the default value must have the same type as the value
  245. that might be inside the `Option<T>`. Using it is dead simple in our case:
  246. ```rust
  247. # fn find(haystack: &str, needle: char) -> Option<usize> {
  248. # for (offset, c) in haystack.char_indices() {
  249. # if c == needle {
  250. # return Some(offset);
  251. # }
  252. # }
  253. # None
  254. # }
  255. #
  256. # fn extension(file_name: &str) -> Option<&str> {
  257. # find(file_name, '.').map(|i| &file_name[i+1..])
  258. # }
  259. fn main() {
  260. assert_eq!(extension("foobar.csv").unwrap_or("rs"), "csv");
  261. assert_eq!(extension("foobar").unwrap_or("rs"), "rs");
  262. }
  263. ```
  264. (Note that `unwrap_or` is [defined as a method][3] on `Option<T>` in the
  265. standard library, so we use that here instead of the free-standing function we
  266. defined above. Don't forget to check out the more general [`unwrap_or_else`][4]
  267. method.)
  268. There is one more combinator that we think is worth paying special attention to:
  269. `and_then`. It makes it easy to compose distinct computations that admit the
  270. *possibility of absence*. For example, much of the code in this section is
  271. about finding an extension given a file name. In order to do this, you first
  272. need the file name which is typically extracted from a file *path*. While most
  273. file paths have a file name, not *all* of them do. For example, `.`, `..` or
  274. `/`.
  275. So, we are tasked with the challenge of finding an extension given a file
  276. *path*. Let's start with explicit case analysis:
  277. ```rust
  278. # fn extension(file_name: &str) -> Option<&str> { None }
  279. fn file_path_ext_explicit(file_path: &str) -> Option<&str> {
  280. match file_name(file_path) {
  281. None => None,
  282. Some(name) => match extension(name) {
  283. None => None,
  284. Some(ext) => Some(ext),
  285. }
  286. }
  287. }
  288. fn file_name(file_path: &str) -> Option<&str> {
  289. // implementation elided
  290. unimplemented!()
  291. }
  292. ```
  293. You might think that we could just use the `map` combinator to reduce the case
  294. analysis, but its type doesn't quite fit. Namely, `map` takes a function that
  295. does something only with the inner value. The result of that function is then
  296. *always* [rewrapped with `Some`](#code-option-map). Instead, we need something
  297. like `map`, but which allows the caller to return another `Option`. Its generic
  298. implementation is even simpler than `map`:
  299. ```rust
  300. fn and_then<F, T, A>(option: Option<T>, f: F) -> Option<A>
  301. where F: FnOnce(T) -> Option<A> {
  302. match option {
  303. None => None,
  304. Some(value) => f(value),
  305. }
  306. }
  307. ```
  308. Now we can rewrite our `file_path_ext` function without explicit case analysis:
  309. ```rust
  310. # fn extension(file_name: &str) -> Option<&str> { None }
  311. # fn file_name(file_path: &str) -> Option<&str> { None }
  312. fn file_path_ext(file_path: &str) -> Option<&str> {
  313. file_name(file_path).and_then(extension)
  314. }
  315. ```
  316. The `Option` type has many other combinators [defined in the standard
  317. library][5]. It is a good idea to skim this list and familiarize
  318. yourself with what's available—they can often reduce case analysis
  319. for you. Familiarizing yourself with these combinators will pay
  320. dividends because many of them are also defined (with similar
  321. semantics) for `Result`, which we will talk about next.
  322. Combinators make using types like `Option` ergonomic because they reduce
  323. explicit case analysis. They are also composable because they permit the caller
  324. to handle the possibility of absence in their own way. Methods like `unwrap`
  325. remove choices because they will panic if `Option<T>` is `None`.
  326. ## The `Result` type
  327. The `Result` type is also
  328. [defined in the standard library][6]:
  329. <span id="code-result-def"></span>
  330. ```rust
  331. enum Result<T, E> {
  332. Ok(T),
  333. Err(E),
  334. }
  335. ```
  336. The `Result` type is a richer version of `Option`. Instead of expressing the
  337. possibility of *absence* like `Option` does, `Result` expresses the possibility
  338. of *error*. Usually, the *error* is used to explain why the execution of some
  339. computation failed. This is a strictly more general form of `Option`. Consider
  340. the following type alias, which is semantically equivalent to the real
  341. `Option<T>` in every way:
  342. ```rust
  343. type Option<T> = Result<T, ()>;
  344. ```
  345. This fixes the second type parameter of `Result` to always be `()` (pronounced
  346. unit or empty tuple). Exactly one value inhabits the `()` type: `()`. (Yup,
  347. the type and value level terms have the same notation!)
  348. The `Result` type is a way of representing one of two possible outcomes in a
  349. computation. By convention, one outcome is meant to be expected or `Ok` while
  350. the other outcome is meant to be unexpected or `Err`.
  351. Just like `Option`, the `Result` type also has an
  352. [`unwrap` method
  353. defined][7]
  354. in the standard library. Let's define it:
  355. ```rust
  356. # enum Result<T, E> { Ok(T), Err(E) }
  357. impl<T, E: ::std::fmt::Debug> Result<T, E> {
  358. fn unwrap(self) -> T {
  359. match self {
  360. Result::Ok(val) => val,
  361. Result::Err(err) =>
  362. panic!("called `Result::unwrap()` on an `Err` value: {:?}", err),
  363. }
  364. }
  365. }
  366. ```
  367. This is effectively the same as our [definition for
  368. `Option::unwrap`](#code-option-def-unwrap), except it includes the
  369. error value in the `panic!` message. This makes debugging easier, but
  370. it also requires us to add a [`Debug`][8] constraint on the `E` type
  371. parameter (which represents our error type). Since the vast majority
  372. of types should satisfy the `Debug` constraint, this tends to work out
  373. in practice. (`Debug` on a type simply means that there's a reasonable
  374. way to print a human readable description of values with that type.)
  375. OK, let's move on to an example.
  376. ### Parsing integers
  377. The Rust standard library makes converting strings to integers dead simple.
  378. It's so easy in fact, that it is very tempting to write something like the
  379. following:
  380. ```rust
  381. fn double_number(number_str: &str) -> i32 {
  382. 2 * number_str.parse::<i32>().unwrap()
  383. }
  384. fn main() {
  385. let n: i32 = double_number("10");
  386. assert_eq!(n, 20);
  387. }
  388. ```
  389. At this point, you should be skeptical of calling `unwrap`. For example, if
  390. the string doesn't parse as a number, you'll get a panic:
  391. ```text
  392. 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
  393. ```
  394. This is rather unsightly, and if this happened inside a library you're
  395. using, you might be understandably annoyed. Instead, we should try to
  396. handle the error in our function and let the caller decide what to
  397. do. This means changing the return type of `double_number`. But to
  398. what? Well, that requires looking at the signature of the [`parse`
  399. method][9] in the standard library:
  400. ```rust,ignore
  401. impl str {
  402. fn parse<F: FromStr>(&self) -> Result<F, F::Err>;
  403. }
  404. ```
  405. Hmm. So we at least know that we need to use a `Result`. Certainly, it's
  406. possible that this could have returned an `Option`. After all, a string either
  407. parses as a number or it doesn't, right? That's certainly a reasonable way to
  408. go, but the implementation internally distinguishes *why* the string didn't
  409. parse as an integer. (Whether it's an empty string, an invalid digit, too big
  410. or too small.) Therefore, using a `Result` makes sense because we want to
  411. provide more information than simply absence. We want to say *why* the
  412. parsing failed. You should try to emulate this line of reasoning when faced
  413. with a choice between `Option` and `Result`. If you can provide detailed error
  414. information, then you probably should. (We'll see more on this later.)
  415. OK, but how do we write our return type? The `parse` method as defined
  416. above is generic over all the different number types defined in the
  417. standard library. We could (and probably should) also make our
  418. function generic, but let's favor explicitness for the moment. We only
  419. care about `i32`, so we need to [find its implementation of
  420. `FromStr`](../std/primitive.i32.html) (do a `CTRL-F` in your browser
  421. for FromStr) and look at its [associated type][10] `Err`. We did
  422. this so we can find the concrete error type. In this case, it's
  423. [`std::num::ParseIntError`](../std/num/struct.ParseIntError.html).
  424. Finally, we can rewrite our function:
  425. ```rust
  426. use std::num::ParseIntError;
  427. fn double_number(number_str: &str) -> Result<i32, ParseIntError> {
  428. match number_str.parse::<i32>() {
  429. Ok(n) => Ok(2 * n),
  430. Err(err) => Err(err),
  431. }
  432. }
  433. fn main() {
  434. match double_number("10") {
  435. Ok(n) => assert_eq!(n, 20),
  436. Err(err) => println!("Error: {:?}", err),
  437. }
  438. }
  439. ```
  440. This is a little better, but now we've written a lot more code! The case
  441. analysis has once again bitten us.
  442. Combinators to the rescue! Just like `Option`, `Result` has lots of combinators
  443. defined as methods. There is a large intersection of common combinators between
  444. `Result` and `Option`. In particular, `map` is part of that intersection:
  445. ```rust
  446. use std::num::ParseIntError;
  447. fn double_number(number_str: &str) -> Result<i32, ParseIntError> {
  448. number_str.parse::<i32>().map(|n| 2 * n)
  449. }
  450. fn main() {
  451. match double_number("10") {
  452. Ok(n) => assert_eq!(n, 20),
  453. Err(err) => println!("Error: {:?}", err),
  454. }
  455. }
  456. ```
  457. The usual suspects are all there for `Result`, including
  458. [`unwrap_or`](../std/result/enum.Result.html#method.unwrap_or) and
  459. [`and_then`](../std/result/enum.Result.html#method.and_then).
  460. Additionally, since `Result` has a second type parameter, there are
  461. combinators that affect only the error type, such as
  462. [`map_err`](../std/result/enum.Result.html#method.map_err) (instead of
  463. `map`) and [`or_else`](../std/result/enum.Result.html#method.or_else)
  464. (instead of `and_then`).
  465. ### The `Result` type alias idiom
  466. In the standard library, you may frequently see types like
  467. `Result<i32>`. But wait, [we defined `Result`](#code-result-def) to
  468. have two type parameters. How can we get away with only specifying
  469. one? The key is to define a `Result` type alias that *fixes* one of
  470. the type parameters to a particular type. Usually the fixed type is
  471. the error type. For example, our previous example parsing integers
  472. could be rewritten like this:
  473. ```rust
  474. use std::num::ParseIntError;
  475. use std::result;
  476. type Result<T> = result::Result<T, ParseIntError>;
  477. fn double_number(number_str: &str) -> Result<i32> {
  478. unimplemented!();
  479. }
  480. ```
  481. Why would we do this? Well, if we have a lot of functions that could return
  482. `ParseIntError`, then it's much more convenient to define an alias that always
  483. uses `ParseIntError` so that we don't have to write it out all the time.
  484. The most prominent place this idiom is used in the standard library is
  485. with [`io::Result`](../std/io/type.Result.html). Typically, one writes
  486. `io::Result<T>`, which makes it clear that you're using the `io`
  487. module's type alias instead of the plain definition from
  488. `std::result`. (This idiom is also used for
  489. [`fmt::Result`](../std/fmt/type.Result.html).)
  490. ## A brief interlude: unwrapping isn't evil
  491. If you've been following along, you might have noticed that I've taken a pretty
  492. hard line against calling methods like `unwrap` that could `panic` and abort
  493. your program. *Generally speaking*, this is good advice.
  494. However, `unwrap` can still be used judiciously. What exactly justifies use of
  495. `unwrap` is somewhat of a grey area and reasonable people can disagree. I'll
  496. summarize some of my *opinions* on the matter.
  497. * **In examples and quick 'n' dirty code.** Sometimes you're writing examples
  498. or a quick program, and error handling simply isn't important. Beating the
  499. convenience of `unwrap` can be hard in such scenarios, so it is very
  500. appealing.
  501. * **When panicking indicates a bug in the program.** When the invariants of
  502. your code should prevent a certain case from happening (like, say, popping
  503. from an empty stack), then panicking can be permissible. This is because it
  504. exposes a bug in your program. This can be explicit, like from an `assert!`
  505. failing, or it could be because your index into an array was out of bounds.
  506. This is probably not an exhaustive list. Moreover, when using an
  507. `Option`, it is often better to use its
  508. [`expect`](../std/option/enum.Option.html#method.expect)
  509. method. `expect` does exactly the same thing as `unwrap`, except it
  510. prints a message you give to `expect`. This makes the resulting panic
  511. a bit nicer to deal with, since it will show your message instead of
  512. called unwrap on a `None` value.
  513. My advice boils down to this: use good judgment. There's a reason why the words
  514. never do X or Y is considered harmful don't appear in my writing. There are
  515. trade offs to all things, and it is up to you as the programmer to determine
  516. what is acceptable for your use cases. My goal is only to help you evaluate
  517. trade offs as accurately as possible.
  518. Now that we've covered the basics of error handling in Rust, and
  519. explained unwrapping, let's start exploring more of the standard
  520. library.
  521. # Working with multiple error types
  522. Thus far, we've looked at error handling where everything was either an
  523. `Option<T>` or a `Result<T, SomeError>`. But what happens when you have both an
  524. `Option` and a `Result`? Or what if you have a `Result<T, Error1>` and a
  525. `Result<T, Error2>`? Handling *composition of distinct error types* is the next
  526. challenge in front of us, and it will be the major theme throughout the rest of
  527. this chapter.
  528. ## Composing `Option` and `Result`
  529. So far, I've talked about combinators defined for `Option` and combinators
  530. defined for `Result`. We can use these combinators to compose results of
  531. different computations without doing explicit case analysis.
  532. Of course, in real code, things aren't always as clean. Sometimes you have a
  533. mix of `Option` and `Result` types. Must we resort to explicit case analysis,
  534. or can we continue using combinators?
  535. For now, let's revisit one of the first examples in this chapter:
  536. ```rust,should_panic
  537. use std::env;
  538. fn main() {
  539. let mut argv = env::args();
  540. let arg: String = argv.nth(1).unwrap(); // error 1
  541. let n: i32 = arg.parse().unwrap(); // error 2
  542. println!("{}", 2 * n);
  543. }
  544. ```
  545. Given our new found knowledge of `Option`, `Result` and their various
  546. combinators, we should try to rewrite this so that errors are handled properly
  547. and the program doesn't panic if there's an error.
  548. The tricky aspect here is that `argv.nth(1)` produces an `Option` while
  549. `arg.parse()` produces a `Result`. These aren't directly composable. When faced
  550. with both an `Option` and a `Result`, the solution is *usually* to convert the
  551. `Option` to a `Result`. In our case, the absence of a command line parameter
  552. (from `env::args()`) means the user didn't invoke the program correctly. We
  553. could just use a `String` to describe the error. Let's try:
  554. <span id="code-error-double-string"></span>
  555. ```rust
  556. use std::env;
  557. fn double_arg(mut argv: env::Args) -> Result<i32, String> {
  558. argv.nth(1)
  559. .ok_or("Please give at least one argument".to_owned())
  560. .and_then(|arg| arg.parse::<i32>().map_err(|err| err.to_string()))
  561. }
  562. fn main() {
  563. match double_arg(env::args()) {
  564. Ok(n) => println!("{}", n),
  565. Err(err) => println!("Error: {}", err),
  566. }
  567. }
  568. ```
  569. There are a couple new things in this example. The first is the use of the
  570. [`Option::ok_or`](../std/option/enum.Option.html#method.ok_or)
  571. combinator. This is one way to convert an `Option` into a `Result`. The
  572. conversion requires you to specify what error to use if `Option` is `None`.
  573. Like the other combinators we've seen, its definition is very simple:
  574. ```rust
  575. fn ok_or<T, E>(option: Option<T>, err: E) -> Result<T, E> {
  576. match option {
  577. Some(val) => Ok(val),
  578. None => Err(err),
  579. }
  580. }
  581. ```
  582. The other new combinator used here is
  583. [`Result::map_err`](../std/result/enum.Result.html#method.map_err).
  584. This is just like `Result::map`, except it maps a function on to the *error*
  585. portion of a `Result` value. If the `Result` is an `Ok(...)` value, then it is
  586. returned unmodified.
  587. We use `map_err` here because it is necessary for the error types to remain
  588. the same (because of our use of `and_then`). Since we chose to convert the
  589. `Option<String>` (from `argv.nth(1)`) to a `Result<String, String>`, we must
  590. also convert the `ParseIntError` from `arg.parse()` to a `String`.
  591. ## The limits of combinators
  592. Doing IO and parsing input is a very common task, and it's one that I
  593. personally have done a lot of in Rust. Therefore, we will use (and continue to
  594. use) IO and various parsing routines to exemplify error handling.
  595. Let's start simple. We are tasked with opening a file, reading all of its
  596. contents and converting its contents to a number. Then we multiply it by `2`
  597. and print the output.
  598. Although I've tried to convince you not to use `unwrap`, it can be useful
  599. to first write your code using `unwrap`. It allows you to focus on your problem
  600. instead of the error handling, and it exposes the points where proper error
  601. handling need to occur. Let's start there so we can get a handle on the code,
  602. and then refactor it to use better error handling.
  603. ```rust,should_panic
  604. use std::fs::File;
  605. use std::io::Read;
  606. use std::path::Path;
  607. fn file_double<P: AsRef<Path>>(file_path: P) -> i32 {
  608. let mut file = File::open(file_path).unwrap(); // error 1
  609. let mut contents = String::new();
  610. file.read_to_string(&mut contents).unwrap(); // error 2
  611. let n: i32 = contents.trim().parse().unwrap(); // error 3
  612. 2 * n
  613. }
  614. fn main() {
  615. let doubled = file_double("foobar");
  616. println!("{}", doubled);
  617. }
  618. ```
  619. (N.B. The `AsRef<Path>` is used because those are the
  620. [same bounds used on
  621. `std::fs::File::open`](../std/fs/struct.File.html#method.open).
  622. This makes it ergonomic to use any kind of string as a file path.)
  623. There are three different errors that can occur here:
  624. 1. A problem opening the file.
  625. 2. A problem reading data from the file.
  626. 3. A problem parsing the data as a number.
  627. The first two problems are described via the
  628. [`std::io::Error`](../std/io/struct.Error.html) type. We know this
  629. because of the return types of
  630. [`std::fs::File::open`](../std/fs/struct.File.html#method.open) and
  631. [`std::io::Read::read_to_string`](../std/io/trait.Read.html#method.read_to_string).
  632. (Note that they both use the [`Result` type alias
  633. idiom](#the-result-type-alias-idiom) described previously. If you
  634. click on the `Result` type, you'll [see the type
  635. alias](../std/io/type.Result.html), and consequently, the underlying
  636. `io::Error` type.) The third problem is described by the
  637. [`std::num::ParseIntError`](../std/num/struct.ParseIntError.html)
  638. type. The `io::Error` type in particular is *pervasive* throughout the
  639. standard library. You will see it again and again.
  640. Let's start the process of refactoring the `file_double` function. To make this
  641. function composable with other components of the program, it should *not* panic
  642. if any of the above error conditions are met. Effectively, this means that the
  643. function should *return an error* if any of its operations fail. Our problem is
  644. that the return type of `file_double` is `i32`, which does not give us any
  645. useful way of reporting an error. Thus, we must start by changing the return
  646. type from `i32` to something else.
  647. The first thing we need to decide: should we use `Option` or `Result`? We
  648. certainly could use `Option` very easily. If any of the three errors occur, we
  649. could simply return `None`. This will work *and it is better than panicking*,
  650. but we can do a lot better. Instead, we should pass some detail about the error
  651. that occurred. Since we want to express the *possibility of error*, we should
  652. use `Result<i32, E>`. But what should `E` be? Since two *different* types of
  653. errors can occur, we need to convert them to a common type. One such type is
  654. `String`. Let's see how that impacts our code:
  655. ```rust
  656. use std::fs::File;
  657. use std::io::Read;
  658. use std::path::Path;
  659. fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
  660. File::open(file_path)
  661. .map_err(|err| err.to_string())
  662. .and_then(|mut file| {
  663. let mut contents = String::new();
  664. file.read_to_string(&mut contents)
  665. .map_err(|err| err.to_string())
  666. .map(|_| contents)
  667. })
  668. .and_then(|contents| {
  669. contents.trim().parse::<i32>()
  670. .map_err(|err| err.to_string())
  671. })
  672. .map(|n| 2 * n)
  673. }
  674. fn main() {
  675. match file_double("foobar") {
  676. Ok(n) => println!("{}", n),
  677. Err(err) => println!("Error: {}", err),
  678. }
  679. }
  680. ```
  681. This code looks a bit hairy. It can take quite a bit of practice before code
  682. like this becomes easy to write. The way we write it is by *following the
  683. types*. As soon as we changed the return type of `file_double` to
  684. `Result<i32, String>`, we had to start looking for the right combinators. In
  685. this case, we only used three different combinators: `and_then`, `map` and
  686. `map_err`.
  687. `and_then` is used to chain multiple computations where each computation could
  688. return an error. After opening the file, there are two more computations that
  689. could fail: reading from the file and parsing the contents as a number.
  690. Correspondingly, there are two calls to `and_then`.
  691. `map` is used to apply a function to the `Ok(...)` value of a `Result`. For
  692. example, the very last call to `map` multiplies the `Ok(...)` value (which is
  693. an `i32`) by `2`. If an error had occurred before that point, this operation
  694. would have been skipped because of how `map` is defined.
  695. `map_err` is the trick that makes all of this work. `map_err` is just like
  696. `map`, except it applies a function to the `Err(...)` value of a `Result`. In
  697. this case, we want to convert all of our errors to one type: `String`. Since
  698. both `io::Error` and `num::ParseIntError` implement `ToString`, we can call the
  699. `to_string()` method to convert them.
  700. With all of that said, the code is still hairy. Mastering use of combinators is
  701. important, but they have their limits. Let's try a different approach: early
  702. returns.
  703. ## Early returns
  704. I'd like to take the code from the previous section and rewrite it using *early
  705. returns*. Early returns let you exit the function early. We can't return early
  706. in `file_double` from inside another closure, so we'll need to revert back to
  707. explicit case analysis.
  708. ```rust
  709. use std::fs::File;
  710. use std::io::Read;
  711. use std::path::Path;
  712. fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
  713. let mut file = match File::open(file_path) {
  714. Ok(file) => file,
  715. Err(err) => return Err(err.to_string()),
  716. };
  717. let mut contents = String::new();
  718. if let Err(err) = file.read_to_string(&mut contents) {
  719. return Err(err.to_string());
  720. }
  721. let n: i32 = match contents.trim().parse() {
  722. Ok(n) => n,
  723. Err(err) => return Err(err.to_string()),
  724. };
  725. Ok(2 * n)
  726. }
  727. fn main() {
  728. match file_double("foobar") {
  729. Ok(n) => println!("{}", n),
  730. Err(err) => println!("Error: {}", err),
  731. }
  732. }
  733. ```
  734. Reasonable people can disagree over whether this code is better that the code
  735. that uses combinators, but if you aren't familiar with the combinator approach,
  736. this code looks simpler to read to me. It uses explicit case analysis with
  737. `match` and `if let`. If an error occurs, it simply stops executing the
  738. function and returns the error (by converting it to a string).
  739. Isn't this a step backwards though? Previously, we said that the key to
  740. ergonomic error handling is reducing explicit case analysis, yet we've reverted
  741. back to explicit case analysis here. It turns out, there are *multiple* ways to
  742. reduce explicit case analysis. Combinators aren't the only way.
  743. ## The `try!` macro
  744. A cornerstone of error handling in Rust is the `try!` macro. The `try!` macro
  745. abstracts case analysis just like combinators, but unlike combinators, it also
  746. abstracts *control flow*. Namely, it can abstract the *early return* pattern
  747. seen above.
  748. Here is a simplified definition of a `try!` macro:
  749. <span id="code-try-def-simple"></span>
  750. ```rust
  751. macro_rules! try {
  752. ($e:expr) => (match $e {
  753. Ok(val) => val,
  754. Err(err) => return Err(err),
  755. });
  756. }
  757. ```
  758. (The [real definition](../std/macro.try!.html) is a bit more
  759. sophisticated. We will address that later.)
  760. Using the `try!` macro makes it very easy to simplify our last example. Since
  761. it does the case analysis and the early return for us, we get tighter code that
  762. is easier to read:
  763. ```rust
  764. use std::fs::File;
  765. use std::io::Read;
  766. use std::path::Path;
  767. fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
  768. let mut file = try!(File::open(file_path).map_err(|e| e.to_string()));
  769. let mut contents = String::new();
  770. try!(file.read_to_string(&mut contents).map_err(|e| e.to_string()));
  771. let n = try!(contents.trim().parse::<i32>().map_err(|e| e.to_string()));
  772. Ok(2 * n)
  773. }
  774. fn main() {
  775. match file_double("foobar") {
  776. Ok(n) => println!("{}", n),
  777. Err(err) => println!("Error: {}", err),
  778. }
  779. }
  780. ```
  781. The `map_err` calls are still necessary given
  782. [our definition of `try!`](#code-try-def-simple).
  783. This is because the error types still need to be converted to `String`.
  784. The good news is that we will soon learn how to remove those `map_err` calls!
  785. The bad news is that we will need to learn a bit more about a couple important
  786. traits in the standard library before we can remove the `map_err` calls.
  787. ## Defining your own error type
  788. Before we dive into some of the standard library error traits, I'd like to wrap
  789. up this section by removing the use of `String` as our error type in the
  790. previous examples.
  791. Using `String` as we did in our previous examples is convenient because it's
  792. easy to convert errors to strings, or even make up your own errors as strings
  793. on the spot. However, using `String` for your errors has some downsides.
  794. The first downside is that the error messages tend to clutter your
  795. code. It's possible to define the error messages elsewhere, but unless
  796. you're unusually disciplined, it is very tempting to embed the error
  797. message into your code. Indeed, we did exactly this in a [previous
  798. example](#code-error-double-string).
  799. The second and more important downside is that `String`s are *lossy*. That is,
  800. if all errors are converted to strings, then the errors we pass to the caller
  801. become completely opaque. The only reasonable thing the caller can do with a
  802. `String` error is show it to the user. Certainly, inspecting the string to
  803. determine the type of error is not robust. (Admittedly, this downside is far
  804. more important inside of a library as opposed to, say, an application.)
  805. For example, the `io::Error` type embeds an
  806. [`io::ErrorKind`](../std/io/enum.ErrorKind.html),
  807. which is *structured data* that represents what went wrong during an IO
  808. operation. This is important because you might want to react differently
  809. depending on the error. (e.g., A `BrokenPipe` error might mean quitting your
  810. program gracefully while a `NotFound` error might mean exiting with an error
  811. code and showing an error to the user.) With `io::ErrorKind`, the caller can
  812. examine the type of an error with case analysis, which is strictly superior
  813. to trying to tease out the details of an error inside of a `String`.
  814. Instead of using a `String` as an error type in our previous example of reading
  815. an integer from a file, we can define our own error type that represents errors
  816. with *structured data*. We endeavor to not drop information from underlying
  817. errors in case the caller wants to inspect the details.
  818. The ideal way to represent *one of many possibilities* is to define our own
  819. sum type using `enum`. In our case, an error is either an `io::Error` or a
  820. `num::ParseIntError`, so a natural definition arises:
  821. ```rust
  822. use std::io;
  823. use std::num;
  824. // We derive `Debug` because all types should probably derive `Debug`.
  825. // This gives us a reasonable human readable description of `CliError` values.
  826. #[derive(Debug)]
  827. enum CliError {
  828. Io(io::Error),
  829. Parse(num::ParseIntError),
  830. }
  831. ```
  832. Tweaking our code is very easy. Instead of converting errors to strings, we
  833. simply convert them to our `CliError` type using the corresponding value
  834. constructor:
  835. ```rust
  836. # #[derive(Debug)]
  837. # enum CliError { Io(::std::io::Error), Parse(::std::num::ParseIntError) }
  838. use std::fs::File;
  839. use std::io::Read;
  840. use std::path::Path;
  841. fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
  842. let mut file = try!(File::open(file_path).map_err(CliError::Io));
  843. let mut contents = String::new();
  844. try!(file.read_to_string(&mut contents).map_err(CliError::Io));
  845. let n: i32 = try!(contents.trim().parse().map_err(CliError::Parse));
  846. Ok(2 * n)
  847. }
  848. fn main() {
  849. match file_double("foobar") {
  850. Ok(n) => println!("{}", n),
  851. Err(err) => println!("Error: {:?}", err),
  852. }
  853. }
  854. ```
  855. The only change here is switching `map_err(|e| e.to_string())` (which converts
  856. errors to strings) to `map_err(CliError::Io)` or `map_err(CliError::Parse)`.
  857. The *caller* gets to decide the level of detail to report to the user. In
  858. effect, using a `String` as an error type removes choices from the caller while
  859. using a custom `enum` error type like `CliError` gives the caller all of the
  860. conveniences as before in addition to *structured data* describing the error.
  861. A rule of thumb is to define your own error type, but a `String` error type
  862. will do in a pinch, particularly if you're writing an application. If you're
  863. writing a library, defining your own error type should be strongly preferred so
  864. that you don't remove choices from the caller unnecessarily.
  865. # Standard library traits used for error handling
  866. The standard library defines two integral traits for error handling:
  867. [`std::error::Error`](../std/error/trait.Error.html) and
  868. [`std::convert::From`](../std/convert/trait.From.html). While `Error`
  869. is designed specifically for generically describing errors, the `From`
  870. trait serves a more general role for converting values between two
  871. distinct types.
  872. ## The `Error` trait
  873. The `Error` trait is [defined in the standard
  874. library](../std/error/trait.Error.html):
  875. ```rust
  876. use std::fmt::{Debug, Display};
  877. trait Error: Debug + Display {
  878. /// A short description of the error.
  879. fn description(&self) -> &str;
  880. /// The lower level cause of this error, if any.
  881. fn cause(&self) -> Option<&Error> { None }
  882. }
  883. ```
  884. This trait is super generic because it is meant to be implemented for *all*
  885. types that represent errors. This will prove useful for writing composable code
  886. as we'll see later. Otherwise, the trait allows you to do at least the
  887. following things:
  888. * Obtain a `Debug` representation of the error.
  889. * Obtain a user-facing `Display` representation of the error.
  890. * Obtain a short description of the error (via the `description` method).
  891. * Inspect the causal chain of an error, if one exists (via the `cause` method).
  892. The first two are a result of `Error` requiring impls for both `Debug` and
  893. `Display`. The latter two are from the two methods defined on `Error`. The
  894. power of `Error` comes from the fact that all error types impl `Error`, which
  895. means errors can be existentially quantified as a
  896. [trait object](../book/trait-objects.html).
  897. This manifests as either `Box<Error>` or `&Error`. Indeed, the `cause` method
  898. returns an `&Error`, which is itself a trait object. We'll revisit the
  899. `Error` trait's utility as a trait object later.
  900. For now, it suffices to show an example implementing the `Error` trait. Let's
  901. use the error type we defined in the
  902. [previous section](#defining-your-own-error-type):
  903. ```rust
  904. use std::io;
  905. use std::num;
  906. // We derive `Debug` because all types should probably derive `Debug`.
  907. // This gives us a reasonable human readable description of `CliError` values.
  908. #[derive(Debug)]
  909. enum CliError {
  910. Io(io::Error),
  911. Parse(num::ParseIntError),
  912. }
  913. ```
  914. This particular error type represents the possibility of two types of errors
  915. occurring: an error dealing with I/O or an error converting a string to a
  916. number. The error could represent as many error types as you want by adding new
  917. variants to the `enum` definition.
  918. Implementing `Error` is pretty straight-forward. It's mostly going to be a lot
  919. explicit case analysis.
  920. ```rust,ignore
  921. use std::error;
  922. use std::fmt;
  923. impl fmt::Display for CliError {
  924. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  925. match *self {
  926. // Both underlying errors already impl `Display`, so we defer to
  927. // their implementations.
  928. CliError::Io(ref err) => write!(f, "IO error: {}", err),
  929. CliError::Parse(ref err) => write!(f, "Parse error: {}", err),
  930. }
  931. }
  932. }
  933. impl error::Error for CliError {
  934. fn description(&self) -> &str {
  935. // Both underlying errors already impl `Error`, so we defer to their
  936. // implementations.
  937. match *self {
  938. CliError::Io(ref err) => err.description(),
  939. CliError::Parse(ref err) => err.description(),
  940. }
  941. }
  942. fn cause(&self) -> Option<&error::Error> {
  943. match *self {
  944. // N.B. Both of these implicitly cast `err` from their concrete
  945. // types (either `&io::Error` or `&num::ParseIntError`)
  946. // to a trait object `&Error`. This works because both error types
  947. // implement `Error`.
  948. CliError::Io(ref err) => Some(err),
  949. CliError::Parse(ref err) => Some(err),
  950. }
  951. }
  952. }
  953. ```
  954. We note that this is a very typical implementation of `Error`: match on your
  955. different error types and satisfy the contracts defined for `description` and
  956. `cause`.
  957. ## The `From` trait
  958. The `std::convert::From` trait is
  959. [defined in the standard
  960. library](../std/convert/trait.From.html):
  961. <span id="code-from-def"></span>
  962. ```rust
  963. trait From<T> {
  964. fn from(T) -> Self;
  965. }
  966. ```
  967. Deliciously simple, yes? `From` is very useful because it gives us a generic
  968. way to talk about conversion *from* a particular type `T` to some other type
  969. (in this case, some other type is the subject of the impl, or `Self`).
  970. The crux of `From` is the
  971. [set of implementations provided by the standard
  972. library](../std/convert/trait.From.html).
  973. Here are a few simple examples demonstrating how `From` works:
  974. ```rust
  975. let string: String = From::from("foo");
  976. let bytes: Vec<u8> = From::from("foo");
  977. let cow: ::std::borrow::Cow<str> = From::from("foo");
  978. ```
  979. OK, so `From` is useful for converting between strings. But what about errors?
  980. It turns out, there is one critical impl:
  981. ```rust,ignore
  982. impl<'a, E: Error + 'a> From<E> for Box<Error + 'a>
  983. ```
  984. This impl says that for *any* type that impls `Error`, we can convert it to a
  985. trait object `Box<Error>`. This may not seem terribly surprising, but it is
  986. useful in a generic context.
  987. Remember the two errors we were dealing with previously? Specifically,
  988. `io::Error` and `num::ParseIntError`. Since both impl `Error`, they work with
  989. `From`:
  990. ```rust
  991. use std::error::Error;
  992. use std::fs;
  993. use std::io;
  994. use std::num;
  995. // We have to jump through some hoops to actually get error values.
  996. let io_err: io::Error = io::Error::last_os_error();
  997. let parse_err: num::ParseIntError = "not a number".parse::<i32>().unwrap_err();
  998. // OK, here are the conversions.
  999. let err1: Box<Error> = From::from(io_err);
  1000. let err2: Box<Error> = From::from(parse_err);
  1001. ```
  1002. There is a really important pattern to recognize here. Both `err1` and `err2`
  1003. have the *same type*. This is because they are existentially quantified types,
  1004. or trait objects. In particular, their underlying type is *erased* from the
  1005. compiler's knowledge, so it truly sees `err1` and `err2` as exactly the same.
  1006. Additionally, we constructed `err1` and `err2` using precisely the same
  1007. function call: `From::from`. This is because `From::from` is overloaded on both
  1008. its argument and its return type.
  1009. This pattern is important because it solves a problem we had earlier: it gives
  1010. us a way to reliably convert errors to the same type using the same function.
  1011. Time to revisit an old friend; the `try!` macro.
  1012. ## The real `try!` macro
  1013. Previously, we presented this definition of `try!`:
  1014. ```rust
  1015. macro_rules! try {
  1016. ($e:expr) => (match $e {
  1017. Ok(val) => val,
  1018. Err(err) => return Err(err),
  1019. });
  1020. }
  1021. ```
  1022. This is not its real definition. Its real definition is
  1023. [in the standard library](../std/macro.try!.html):
  1024. <span id="code-try-def"></span>
  1025. ```rust
  1026. macro_rules! try {
  1027. ($e:expr) => (match $e {
  1028. Ok(val) => val,
  1029. Err(err) => return Err(::std::convert::From::from(err)),
  1030. });
  1031. }
  1032. ```
  1033. There's one tiny but powerful change: the error value is passed through
  1034. `From::from`. This makes the `try!` macro a lot more powerful because it gives
  1035. you automatic type conversion for free.
  1036. Armed with our more powerful `try!` macro, let's take a look at code we wrote
  1037. previously to read a file and convert its contents to an integer:
  1038. ```rust
  1039. use std::fs::File;
  1040. use std::io::Read;
  1041. use std::path::Path;
  1042. fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
  1043. let mut file = try!(File::open(file_path).map_err(|e| e.to_string()));
  1044. let mut contents = String::new();
  1045. try!(file.read_to_string(&mut contents).map_err(|e| e.to_string()));
  1046. let n = try!(contents.trim().parse::<i32>().map_err(|e| e.to_string()));
  1047. Ok(2 * n)
  1048. }
  1049. ```
  1050. Earlier, we promised that we could get rid of the `map_err` calls. Indeed, all
  1051. we have to do is pick a type that `From` works with. As we saw in the previous
  1052. section, `From` has an impl that lets it convert any error type into a
  1053. `Box<Error>`:
  1054. ```rust
  1055. use std::error::Error;
  1056. use std::fs::File;
  1057. use std::io::Read;
  1058. use std::path::Path;
  1059. fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, Box<Error>> {
  1060. let mut file = try!(File::open(file_path));
  1061. let mut contents = String::new();
  1062. try!(file.read_to_string(&mut contents));
  1063. let n = try!(contents.trim().parse::<i32>());
  1064. Ok(2 * n)
  1065. }
  1066. ```
  1067. We are getting very close to ideal error handling. Our code has very little
  1068. overhead as a result from error handling because the `try!` macro encapsulates
  1069. three things simultaneously:
  1070. 1. Case analysis.
  1071. 2. Control flow.
  1072. 3. Error type conversion.
  1073. When all three things are combined, we get code that is unencumbered by
  1074. combinators, calls to `unwrap` or case analysis.
  1075. There's one little nit left: the `Box<Error>` type is *opaque*. If we
  1076. return a `Box<Error>` to the caller, the caller can't (easily) inspect
  1077. underlying error type. The situation is certainly better than `String`
  1078. because the caller can call methods like
  1079. [`description`](../std/error/trait.Error.html#tymethod.description)
  1080. and [`cause`](../std/error/trait.Error.html#method.cause), but the
  1081. limitation remains: `Box<Error>` is opaque. (N.B. This isn't entirely
  1082. true because Rust does have runtime reflection, which is useful in
  1083. some scenarios that are [beyond the scope of this
  1084. chapter](https://crates.io/crates/error).)
  1085. It's time to revisit our custom `CliError` type and tie everything together.
  1086. ## Composing custom error types
  1087. In the last section, we looked at the real `try!` macro and how it does
  1088. automatic type conversion for us by calling `From::from` on the error value.
  1089. In particular, we converted errors to `Box<Error>`, which works, but the type
  1090. is opaque to callers.
  1091. To fix this, we use the same remedy that we're already familiar with: a custom
  1092. error type. Once again, here is the code that reads the contents of a file and
  1093. converts it to an integer:
  1094. ```rust
  1095. use std::fs::File;
  1096. use std::io::{self, Read};
  1097. use std::num;
  1098. use std::path::Path;
  1099. // We derive `Debug` because all types should probably derive `Debug`.
  1100. // This gives us a reasonable human readable description of `CliError` values.
  1101. #[derive(Debug)]
  1102. enum CliError {
  1103. Io(io::Error),
  1104. Parse(num::ParseIntError),
  1105. }
  1106. fn file_double_verbose<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
  1107. let mut file = try!(File::open(file_path).map_err(CliError::Io));
  1108. let mut contents = String::new();
  1109. try!(file.read_to_string(&mut contents).map_err(CliError::Io));
  1110. let n: i32 = try!(contents.trim().parse().map_err(CliError::Parse));
  1111. Ok(2 * n)
  1112. }
  1113. ```
  1114. Notice that we still have the calls to `map_err`. Why? Well, recall the
  1115. definitions of [`try!`](#code-try-def) and [`From`](#code-from-def). The
  1116. problem is that there is no `From` impl that allows us to convert from error
  1117. types like `io::Error` and `num::ParseIntError` to our own custom `CliError`.
  1118. Of course, it is easy to fix this! Since we defined `CliError`, we can impl
  1119. `From` with it:
  1120. ```rust
  1121. # #[derive(Debug)]
  1122. # enum CliError { Io(io::Error), Parse(num::ParseIntError) }
  1123. use std::io;
  1124. use std::num;
  1125. impl From<io::Error> for CliError {
  1126. fn from(err: io::Error) -> CliError {
  1127. CliError::Io(err)
  1128. }
  1129. }
  1130. impl From<num::ParseIntError> for CliError {
  1131. fn from(err: num::ParseIntError) -> CliError {
  1132. CliError::Parse(err)
  1133. }
  1134. }
  1135. ```
  1136. All these impls are doing is teaching `From` how to create a `CliError` from
  1137. other error types. In our case, construction is as simple as invoking the
  1138. corresponding value constructor. Indeed, it is *typically* this easy.
  1139. We can finally rewrite `file_double`:
  1140. ```rust
  1141. # use std::io;
  1142. # use std::num;
  1143. # enum CliError { Io(::std::io::Error), Parse(::std::num::ParseIntError) }
  1144. # impl From<io::Error> for CliError {
  1145. # fn from(err: io::Error) -> CliError { CliError::Io(err) }
  1146. # }
  1147. # impl From<num::ParseIntError> for CliError {
  1148. # fn from(err: num::ParseIntError) -> CliError { CliError::Parse(err) }
  1149. # }
  1150. use std::fs::File;
  1151. use std::io::Read;
  1152. use std::path::Path;
  1153. fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, CliError> {
  1154. let mut file = try!(File::open(file_path));
  1155. let mut contents = String::new();
  1156. try!(file.read_to_string(&mut contents));
  1157. let n: i32 = try!(contents.trim().parse());
  1158. Ok(2 * n)
  1159. }
  1160. ```
  1161. The only thing we did here was remove the calls to `map_err`. They are no
  1162. longer needed because the `try!` macro invokes `From::from` on the error value.
  1163. This works because we've provided `From` impls for all the error types that
  1164. could appear.
  1165. If we modified our `file_double` function to perform some other operation, say,
  1166. convert a string to a float, then we'd need to add a new variant to our error
  1167. type:
  1168. ```rust
  1169. use std::io;
  1170. use std::num;
  1171. enum CliError {
  1172. Io(io::Error),
  1173. ParseInt(num::ParseIntError),
  1174. ParseFloat(num::ParseFloatError),
  1175. }
  1176. ```
  1177. And add a new `From` impl:
  1178. ```rust
  1179. # enum CliError {
  1180. # Io(::std::io::Error),
  1181. # ParseInt(num::ParseIntError),
  1182. # ParseFloat(num::ParseFloatError),
  1183. # }
  1184. use std::num;
  1185. impl From<num::ParseFloatError> for CliError {
  1186. fn from(err: num::ParseFloatError) -> CliError {
  1187. CliError::ParseFloat(err)
  1188. }
  1189. }
  1190. ```
  1191. And that's it!
  1192. ## Advice for library writers
  1193. If your library needs to report custom errors, then you should
  1194. probably define your own error type. It's up to you whether or not to
  1195. expose its representation (like
  1196. [`ErrorKind`](../std/io/enum.ErrorKind.html)) or keep it hidden (like
  1197. [`ParseIntError`](../std/num/struct.ParseIntError.html)). Regardless
  1198. of how you do it, it's usually good practice to at least provide some
  1199. information about the error beyond just its `String`
  1200. representation. But certainly, this will vary depending on use cases.
  1201. At a minimum, you should probably implement the
  1202. [`Error`](../std/error/trait.Error.html)
  1203. trait. This will give users of your library some minimum flexibility for
  1204. [composing errors](#the-real-try-macro). Implementing the `Error` trait also
  1205. means that users are guaranteed the ability to obtain a string representation
  1206. of an error (because it requires impls for both `fmt::Debug` and
  1207. `fmt::Display`).
  1208. Beyond that, it can also be useful to provide implementations of `From` on your
  1209. error types. This allows you (the library author) and your users to
  1210. [compose more detailed errors](#composing-custom-error-types). For example,
  1211. [`csv::Error`](http://burntsushi.net/rustdoc/csv/enum.Error.html)
  1212. provides `From` impls for both `io::Error` and `byteorder::Error`.
  1213. Finally, depending on your tastes, you may also want to define a
  1214. [`Result` type alias](#the-result-type-alias-idiom), particularly if your
  1215. library defines a single error type. This is used in the standard library
  1216. for [`io::Result`](../std/io/type.Result.html)
  1217. and [`fmt::Result`](../std/fmt/type.Result.html).
  1218. # Case study: A program to read population data
  1219. This chapter was long, and depending on your background, it might be
  1220. rather dense. While there is plenty of example code to go along with
  1221. the prose, most of it was specifically designed to be pedagogical. So,
  1222. we're going to do something new: a case study.
  1223. For this, we're going to build up a command line program that lets you
  1224. query world population data. The objective is simple: you give it a location
  1225. and it will tell you the population. Despite the simplicity, there is a lot
  1226. that can go wrong!
  1227. The data we'll be using comes from the [Data Science
  1228. Toolkit][11]. I've prepared some data from it for this exercise. You
  1229. can either grab the [world population data][12] (41MB gzip compressed,
  1230. 145MB uncompressed) or just the [US population data][13] (2.2MB gzip
  1231. compressed, 7.2MB uncompressed).
  1232. Up until now, we've kept the code limited to Rust's standard library. For a real
  1233. task like this though, we'll want to at least use something to parse CSV data,
  1234. parse the program arguments and decode that stuff into Rust types automatically. For that, we'll use the
  1235. [`csv`](https://crates.io/crates/csv),
  1236. and [`rustc-serialize`](https://crates.io/crates/rustc-serialize) crates.
  1237. ## Initial setup
  1238. We're not going to spend a lot of time on setting up a project with
  1239. Cargo because it is already covered well in [the Cargo
  1240. chapter](../book/hello-cargo.html) and [Cargo's documentation][14].
  1241. To get started from scratch, run `cargo new --bin city-pop` and make sure your
  1242. `Cargo.toml` looks something like this:
  1243. ```text
  1244. [package]
  1245. name = "city-pop"
  1246. version = "0.1.0"
  1247. authors = ["Andrew Gallant <jamslam@gmail.com>"]
  1248. [[bin]]
  1249. name = "city-pop"
  1250. [dependencies]
  1251. csv = "0.*"
  1252. rustc-serialize = "0.*"
  1253. getopts = "0.*"
  1254. ```
  1255. You should already be able to run:
  1256. ```text
  1257. cargo build --release
  1258. ./target/release/city-pop
  1259. # Outputs: Hello, world!
  1260. ```
  1261. ## Argument parsing
  1262. Let's get argument parsing out of the way. We won't go into too much
  1263. detail on Getopts, but there is [some good documentation][15]
  1264. describing it. The short story is that Getopts generates an argument
  1265. parser and a help message from a vector of options (The fact that it
  1266. is a vector is hidden behind a struct and a set of methods). Once the
  1267. parsing is done, we can decode the program arguments into a Rust
  1268. struct. From there, we can get information about the flags, for
  1269. instance, whether they were passed in, and what arguments they
  1270. had. Here's our program with the appropriate `extern crate`
  1271. statements, and the basic argument setup for Getopts:
  1272. ```rust,ignore
  1273. extern crate getopts;
  1274. extern crate rustc_serialize;
  1275. use getopts::Options;
  1276. use std::env;
  1277. fn print_usage(program: &str, opts: Options) {
  1278. println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
  1279. }
  1280. fn main() {
  1281. let args: Vec<String> = env::args().collect();
  1282. let program = args[0].clone();
  1283. let mut opts = Options::new();
  1284. opts.optflag("h", "help", "Show this usage message.");
  1285. let matches = match opts.parse(&args[1..]) {
  1286. Ok(m) => { m }
  1287. Err(e) => { panic!(e.to_string()) }
  1288. };
  1289. if matches.opt_present("h") {
  1290. print_usage(&program, opts);
  1291. return;
  1292. }
  1293. let data_path = args[1].clone();
  1294. let city = args[2].clone();
  1295. // Do stuff with information
  1296. }
  1297. ```
  1298. First, we get a vector of the arguments passed into our program. We
  1299. then store the first one, knowing that it is our program's name. Once
  1300. that's done, we set up our argument flags, in this case a simplistic
  1301. help message flag. Once we have the argument flags set up, we use
  1302. `Options.parse` to parse the argument vector (starting from index one,
  1303. because index 0 is the program name). If this was successful, we
  1304. assign matches to the parsed object, if not, we panic. Once past that,
  1305. we test if the user passed in the help flag, and if so print the usage
  1306. message. The option help messages are constructed by Getopts, so all
  1307. we have to do to print the usage message is tell it what we want it to
  1308. print for the program name and template. If the user has not passed in
  1309. the help flag, we assign the proper variables to their corresponding
  1310. arguments.
  1311. ## Writing the logic
  1312. We all write code differently, but error handling is usually the last thing we
  1313. want to think about. This isn't great for the overall design of a program, but
  1314. it can be useful for rapid prototyping. Because Rust forces us to be explicit
  1315. about error handling (by making us call `unwrap`), it is easy to see which
  1316. parts of our program can cause errors.
  1317. In this case study, the logic is really simple. All we need to do is parse the
  1318. CSV data given to us and print out a field in matching rows. Let's do it. (Make
  1319. sure to add `extern crate csv;` to the top of your file.)
  1320. ```rust,ignore
  1321. // This struct represents the data in each row of the CSV file.
  1322. // Type based decoding absolves us of a lot of the nitty gritty error
  1323. // handling, like parsing strings as integers or floats.
  1324. #[derive(Debug, RustcDecodable)]
  1325. struct Row {
  1326. country: String,
  1327. city: String,
  1328. accent_city: String,
  1329. region: String,
  1330. // Not every row has data for the population, latitude or longitude!
  1331. // So we express them as `Option` types, which admits the possibility of
  1332. // absence. The CSV parser will fill in the correct value for us.
  1333. population: Option<u64>,
  1334. latitude: Option<f64>,
  1335. longitude: Option<f64>,
  1336. }
  1337. fn print_usage(program: &str, opts: Options) {
  1338. println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
  1339. }
  1340. fn main() {
  1341. let args: Vec<String> = env::args().collect();
  1342. let program = args[0].clone();
  1343. let mut opts = Options::new();
  1344. opts.optflag("h", "help", "Show this usage message.");
  1345. let matches = match opts.parse(&args[1..]) {
  1346. Ok(m) => { m }
  1347. Err(e) => { panic!(e.to_string()) }
  1348. };
  1349. if matches.opt_present("h") {
  1350. print_usage(&program, opts);
  1351. return;
  1352. }
  1353. let data_file = args[1].clone();
  1354. let data_path = Path::new(&data_file);
  1355. let city = args[2].clone();
  1356. let file = fs::File::open(data_path).unwrap();
  1357. let mut rdr = csv::Reader::from_reader(file);
  1358. for row in rdr.decode::<Row>() {
  1359. let row = row.unwrap();
  1360. if row.city == city {
  1361. println!("{}, {}: {:?}",
  1362. row.city, row.country,
  1363. row.population.expect("population count"));
  1364. }
  1365. }
  1366. }
  1367. ```
  1368. Let's outline the errors. We can start with the obvious: the three places that
  1369. `unwrap` is called:
  1370. 1. [`fs::File::open`](../std/fs/struct.File.html#method.open)
  1371. can return an
  1372. [`io::Error`](../std/io/struct.Error.html).
  1373. 2. [`csv::Reader::decode`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.decode)
  1374. decodes one record at a time, and
  1375. [decoding a
  1376. record](http://burntsushi.net/rustdoc/csv/struct.DecodedRecords.html)
  1377. (look at the `Item` associated type on the `Iterator` impl)
  1378. can produce a
  1379. [`csv::Error`](http://burntsushi.net/rustdoc/csv/enum.Error.html).
  1380. 3. If `row.population` is `None`, then calling `expect` will panic.
  1381. Are there any others? What if we can't find a matching city? Tools like `grep`
  1382. will return an error code, so we probably should too. So we have logic errors
  1383. specific to our problem, IO errors and CSV parsing errors. We're going to
  1384. explore two different ways to approach handling these errors.
  1385. I'd like to start with `Box<Error>`. Later, we'll see how defining our own
  1386. error type can be useful.
  1387. ## Error handling with `Box<Error>`
  1388. `Box<Error>` is nice because it *just works*. You don't need to define your own
  1389. error types and you don't need any `From` implementations. The downside is that
  1390. since `Box<Error>` is a trait object, it *erases the type*, which means the
  1391. compiler can no longer reason about its underlying type.
  1392. [Previously](#the-limits-of-combinators) we started refactoring our code by
  1393. changing the type of our function from `T` to `Result<T, OurErrorType>`. In
  1394. this case, `OurErrorType` is just `Box<Error>`. But what's `T`? And can we add
  1395. a return type to `main`?
  1396. The answer to the second question is no, we can't. That means we'll need to
  1397. write a new function. But what is `T`? The simplest thing we can do is to
  1398. return a list of matching `Row` values as a `Vec<Row>`. (Better code would
  1399. return an iterator, but that is left as an exercise to the reader.)
  1400. Let's refactor our code into its own function, but keep the calls to `unwrap`.
  1401. Note that we opt to handle the possibility of a missing population count by
  1402. simply ignoring that row.
  1403. ```rust,ignore
  1404. struct Row {
  1405. // unchanged
  1406. }
  1407. struct PopulationCount {
  1408. city: String,
  1409. country: String,
  1410. // This is no longer an `Option` because values of this type are only
  1411. // constructed if they have a population count.
  1412. count: u64,
  1413. }
  1414. fn print_usage(program: &str, opts: Options) {
  1415. println!("{}", opts.usage(&format!("Usage: {} [options] <data-path> <city>", program)));
  1416. }
  1417. fn search<P: AsRef<Path>>(file_path: P, city: &str) -> Vec<PopulationCount> {
  1418. let mut found = vec![];
  1419. let file = fs::File::open(file_path).unwrap();
  1420. let mut rdr = csv::Reader::from_reader(file);
  1421. for row in rdr.decode::<Row>() {
  1422. let row = row.unwrap();
  1423. match row.population {
  1424. None => { } // skip it
  1425. Some(count) => if row.city == city {
  1426. found.push(PopulationCount {
  1427. city: row.city,
  1428. country: row.country,
  1429. count: count,
  1430. });
  1431. },
  1432. }
  1433. }
  1434. found
  1435. }
  1436. fn main() {
  1437. let args: Vec<String> = env::args().collect();
  1438. let program = args[0].clone();
  1439. let mut opts = Options::new();
  1440. opts.optflag("h", "help", "Show this usage message.");
  1441. let matches = match opts.parse(&args[1..]) {
  1442. Ok(m) => { m }
  1443. Err(e) => { panic!(e.to_string()) }
  1444. };
  1445. if matches.opt_present("h") {
  1446. print_usage(&program, opts);
  1447. return;
  1448. }
  1449. let data_file = args[1].clone();
  1450. let data_path = Path::new(&data_file);
  1451. let city = args[2].clone();
  1452. for pop in search(&data_path, &city) {
  1453. println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
  1454. }
  1455. }
  1456. ```
  1457. While we got rid of one use of `expect` (which is a nicer variant of `unwrap`),
  1458. we still should handle the absence of any search results.
  1459. To convert this to proper error handling, we need to do the following:
  1460. 1. Change the return type of `search` to be `Result<Vec<PopulationCount>,
  1461. Box<Error>>`.
  1462. 2. Use the [`try!` macro](#code-try-def) so that errors are returned to the
  1463. caller instead of panicking the program.
  1464. 3. Handle the error in `main`.
  1465. Let's try it:
  1466. ```rust,ignore
  1467. fn search<P: AsRef<Path>>
  1468. (file_path: P, city: &str)
  1469. -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
  1470. let mut found = vec![];
  1471. let file = try!(fs::File::open(file_path));
  1472. let mut rdr = csv::Reader::from_reader(file);
  1473. for row in rdr.decode::<Row>() {
  1474. let row = try!(row);
  1475. match row.population {
  1476. None => { } // skip it
  1477. Some(count) => if row.city == city {
  1478. found.push(PopulationCount {
  1479. city: row.city,
  1480. country: row.country,
  1481. count: count,
  1482. });
  1483. },
  1484. }
  1485. }
  1486. if found.is_empty() {
  1487. Err(From::from("No matching cities with a population were found."))
  1488. } else {
  1489. Ok(found)
  1490. }
  1491. }
  1492. ```
  1493. Instead of `x.unwrap()`, we now have `try!(x)`. Since our function returns a
  1494. `Result<T, E>`, the `try!` macro will return early from the function if an
  1495. error occurs.
  1496. There is one big gotcha in this code: we used `Box<Error + Send + Sync>`
  1497. instead of `Box<Error>`. We did this so we could convert a plain string to an
  1498. error type. We need these extra bounds so that we can use the
  1499. [corresponding `From`
  1500. impls](../std/convert/trait.From.html):
  1501. ```rust,ignore
  1502. // We are making use of this impl in the code above, since we call `From::from`
  1503. // on a `&'static str`.
  1504. impl<'a, 'b> From<&'b str> for Box<Error + Send + Sync + 'a>
  1505. // But this is also useful when you need to allocate a new string for an
  1506. // error message, usually with `format!`.
  1507. impl From<String> for Box<Error + Send + Sync>
  1508. ```
  1509. Since `search` now returns a `Result<T, E>`, `main` should use case analysis
  1510. when calling `search`:
  1511. ```rust,ignore
  1512. ...
  1513. match search(&data_file, &city) {
  1514. Ok(pops) => {
  1515. for pop in pops {
  1516. println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
  1517. }
  1518. }
  1519. Err(err) => println!("{}", err)
  1520. }
  1521. ...
  1522. ```
  1523. Now that we've seen how to do proper error handling with `Box<Error>`, let's
  1524. try a different approach with our own custom error type. But first, let's take
  1525. a quick break from error handling and add support for reading from `stdin`.
  1526. ## Reading from stdin
  1527. In our program, we accept a single file for input and do one pass over the
  1528. data. This means we probably should be able to accept input on stdin. But maybe
  1529. we like the current format too—so let's have both!
  1530. Adding support for stdin is actually quite easy. There are only three things we
  1531. have to do:
  1532. 1. Tweak the program arguments so that a single parameter—the
  1533. city—can be accepted while the population data is read from stdin.
  1534. 2. Modify the program so that an option `-f` can take the file, if it
  1535. is not passed into stdin.
  1536. 3. Modify the `search` function to take an *optional* file path. When `None`,
  1537. it should know to read from stdin.
  1538. First, here's the new usage:
  1539. ```rust,ignore
  1540. fn print_usage(program: &str, opts: Options) {
  1541. println!("{}", opts.usage(&format!("Usage: {} [options] <city>", program)));
  1542. }
  1543. ```
  1544. The next part is going to be only a little harder:
  1545. ```rust,ignore
  1546. ...
  1547. let mut opts = Options::new();
  1548. opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
  1549. opts.optflag("h", "help", "Show this usage message.");
  1550. ...
  1551. let file = matches.opt_str("f");
  1552. let data_file = file.as_ref().map(Path::new);
  1553. let city = if !matches.free.is_empty() {
  1554. matches.free[0].clone()
  1555. } else {
  1556. print_usage(&program, opts);
  1557. return;
  1558. };
  1559. for pop in search(&data_file, &city) {
  1560. println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
  1561. }
  1562. ...
  1563. ```
  1564. In this piece of code, we take `file` (which has the type
  1565. `Option<String>`), and convert it to a type that `search` can use, in
  1566. this case, `&Option<AsRef<Path>>`. To do this, we take a reference of
  1567. file, and map `Path::new` onto it. In this case, `as_ref()` converts
  1568. the `Option<String>` into an `Option<&str>`, and from there, we can
  1569. execute `Path::new` to the content of the optional, and return the
  1570. optional of the new value. Once we have that, it is a simple matter of
  1571. getting the `city` argument and executing `search`.
  1572. Modifying `search` is slightly trickier. The `csv` crate can build a
  1573. parser out of
  1574. [any type that implements `io::Read`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.from_reader).
  1575. But how can we use the same code over both types? There's actually a
  1576. couple ways we could go about this. One way is to write `search` such
  1577. that it is generic on some type parameter `R` that satisfies
  1578. `io::Read`. Another way is to just use trait objects:
  1579. ```rust,ignore
  1580. fn search<P: AsRef<Path>>
  1581. (file_path: &Option<P>, city: &str)
  1582. -> Result<Vec<PopulationCount>, Box<Error+Send+Sync>> {
  1583. let mut found = vec![];
  1584. let input: Box<io::Read> = match *file_path {
  1585. None => Box::new(io::stdin()),
  1586. Some(ref file_path) => Box::new(try!(fs::File::open(file_path))),
  1587. };
  1588. let mut rdr = csv::Reader::from_reader(input);
  1589. // The rest remains unchanged!
  1590. }
  1591. ```
  1592. ## Error handling with a custom type
  1593. Previously, we learned how to
  1594. [compose errors using a custom error type](#composing-custom-error-types).
  1595. We did this by defining our error type as an `enum` and implementing `Error`
  1596. and `From`.
  1597. Since we have three distinct errors (IO, CSV parsing and not found), let's
  1598. define an `enum` with three variants:
  1599. ```rust,ignore
  1600. #[derive(Debug)]
  1601. enum CliError {
  1602. Io(io::Error),
  1603. Csv(csv::Error),
  1604. NotFound,
  1605. }
  1606. ```
  1607. And now for impls on `Display` and `Error`:
  1608. ```rust,ignore
  1609. impl fmt::Display for CliError {
  1610. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  1611. match *self {
  1612. CliError::Io(ref err) => err.fmt(f),
  1613. CliError::Csv(ref err) => err.fmt(f),
  1614. CliError::NotFound => write!(f, "No matching cities with a \
  1615. population were found."),
  1616. }
  1617. }
  1618. }
  1619. impl Error for CliError {
  1620. fn description(&self) -> &str {
  1621. match *self {
  1622. CliError::Io(ref err) => err.description(),
  1623. CliError::Csv(ref err) => err.description(),
  1624. CliError::NotFound => "not found",
  1625. }
  1626. }
  1627. }
  1628. ```
  1629. Before we can use our `CliError` type in our `search` function, we need to
  1630. provide a couple `From` impls. How do we know which impls to provide? Well,
  1631. we'll need to convert from both `io::Error` and `csv::Error` to `CliError`.
  1632. Those are the only external errors, so we'll only need two `From` impls for
  1633. now:
  1634. ```rust,ignore
  1635. impl From<io::Error> for CliError {
  1636. fn from(err: io::Error) -> CliError {
  1637. CliError::Io(err)
  1638. }
  1639. }
  1640. impl From<csv::Error> for CliError {
  1641. fn from(err: csv::Error) -> CliError {
  1642. CliError::Csv(err)
  1643. }
  1644. }
  1645. ```
  1646. The `From` impls are important because of how
  1647. [`try!` is defined](#code-try-def). In particular, if an error occurs,
  1648. `From::from` is called on the error, which in this case, will convert it to our
  1649. own error type `CliError`.
  1650. With the `From` impls done, we only need to make two small tweaks to our
  1651. `search` function: the return type and the “not found” error. Here it is in
  1652. full:
  1653. ```rust,ignore
  1654. fn search<P: AsRef<Path>>
  1655. (file_path: &Option<P>, city: &str)
  1656. -> Result<Vec<PopulationCount>, CliError> {
  1657. let mut found = vec![];
  1658. let input: Box<io::Read> = match *file_path {
  1659. None => Box::new(io::stdin()),
  1660. Some(ref file_path) => Box::new(try!(fs::File::open(file_path))),
  1661. };
  1662. let mut rdr = csv::Reader::from_reader(input);
  1663. for row in rdr.decode::<Row>() {
  1664. let row = try!(row);
  1665. match row.population {
  1666. None => { } // skip it
  1667. Some(count) => if row.city == city {
  1668. found.push(PopulationCount {
  1669. city: row.city,
  1670. country: row.country,
  1671. count: count,
  1672. });
  1673. },
  1674. }
  1675. }
  1676. if found.is_empty() {
  1677. Err(CliError::NotFound)
  1678. } else {
  1679. Ok(found)
  1680. }
  1681. }
  1682. ```
  1683. No other changes are necessary.
  1684. ## Adding functionality
  1685. Writing generic code is great, because generalizing stuff is cool, and
  1686. it can then be useful later. But sometimes, the juice isn't worth the
  1687. squeeze. Look at what we just did in the previous step:
  1688. 1. Defined a new error type.
  1689. 2. Added impls for `Error`, `Display` and two for `From`.
  1690. The big downside here is that our program didn't improve a whole lot.
  1691. There is quite a bit of overhead to representing errors with `enum`s,
  1692. especially in short programs like this.
  1693. *One* useful aspect of using a custom error type like we've done here is that
  1694. the `main` function can now choose to handle errors differently. Previously,
  1695. with `Box<Error>`, it didn't have much of a choice: just print the message.
  1696. We're still doing that here, but what if we wanted to, say, add a `--quiet`
  1697. flag? The `--quiet` flag should silence any verbose output.
  1698. Right now, if the program doesn't find a match, it will output a message saying
  1699. so. This can be a little clumsy, especially if you intend for the program to
  1700. be used in shell scripts.
  1701. So let's start by adding the flags. Like before, we need to tweak the usage
  1702. string and add a flag to the Option variable. Once we've done that, Getopts does the rest:
  1703. ```rust,ignore
  1704. ...
  1705. let mut opts = Options::new();
  1706. opts.optopt("f", "file", "Choose an input file, instead of using STDIN.", "NAME");
  1707. opts.optflag("h", "help", "Show this usage message.");
  1708. opts.optflag("q", "quiet", "Silences errors and warnings.");
  1709. ...
  1710. ```
  1711. Now we just need to implement our “quiet” functionality. This requires us to
  1712. tweak the case analysis in `main`:
  1713. ```rust,ignore
  1714. match search(&args.arg_data_path, &args.arg_city) {
  1715. Err(CliError::NotFound) if args.flag_quiet => process::exit(1),
  1716. Err(err) => panic!("{}", err),
  1717. Ok(pops) => for pop in pops {
  1718. println!("{}, {}: {:?}", pop.city, pop.country, pop.count);
  1719. }
  1720. }
  1721. ```
  1722. Certainly, we don't want to be quiet if there was an IO error or if the data
  1723. failed to parse. Therefore, we use case analysis to check if the error type is
  1724. `NotFound` *and* if `--quiet` has been enabled. If the search failed, we still
  1725. quit with an exit code (following `grep`'s convention).
  1726. If we had stuck with `Box<Error>`, then it would be pretty tricky to implement
  1727. the `--quiet` functionality.
  1728. This pretty much sums up our case study. From here, you should be ready to go
  1729. out into the world and write your own programs and libraries with proper error
  1730. handling.
  1731. # The Short Story
  1732. Since this chapter is long, it is useful to have a quick summary for error
  1733. handling in Rust. These are some good “rules of thumb." They are emphatically
  1734. *not* commandments. There are probably good reasons to break every one of these
  1735. heuristics!
  1736. * If you're writing short example code that would be overburdened by error
  1737. handling, it's probably just fine to use `unwrap` (whether that's
  1738. [`Result::unwrap`](../std/result/enum.Result.html#method.unwrap),
  1739. [`Option::unwrap`](../std/option/enum.Option.html#method.unwrap)
  1740. or preferably
  1741. [`Option::expect`](../std/option/enum.Option.html#method.expect)).
  1742. Consumers of your code should know to use proper error handling. (If they
  1743. don't, send them here!)
  1744. * If you're writing a quick 'n' dirty program, don't feel ashamed if you use
  1745. `unwrap`. Be warned: if it winds up in someone else's hands, don't be
  1746. surprised if they are agitated by poor error messages!
  1747. * If you're writing a quick 'n' dirty program and feel ashamed about panicking
  1748. anyway, then using either a `String` or a `Box<Error + Send + Sync>` for your
  1749. error type (the `Box<Error + Send + Sync>` type is because of the
  1750. [available `From` impls](../std/convert/trait.From.html)).
  1751. * Otherwise, in a program, define your own error types with appropriate
  1752. [`From`](../std/convert/trait.From.html)
  1753. and
  1754. [`Error`](../std/error/trait.Error.html)
  1755. impls to make the [`try!`](../std/macro.try!.html)
  1756. macro more ergonomic.
  1757. * If you're writing a library and your code can produce errors, define your own
  1758. error type and implement the
  1759. [`std::error::Error`](../std/error/trait.Error.html)
  1760. trait. Where appropriate, implement
  1761. [`From`](../std/convert/trait.From.html) to make both
  1762. your library code and the caller's code easier to write. (Because of Rust's
  1763. coherence rules, callers will not be able to impl `From` on your error type,
  1764. so your library should do it.)
  1765. * Learn the combinators defined on
  1766. [`Option`](../std/option/enum.Option.html)
  1767. and
  1768. [`Result`](../std/result/enum.Result.html).
  1769. Using them exclusively can be a bit tiring at times, but I've personally
  1770. found a healthy mix of `try!` and combinators to be quite appealing.
  1771. `and_then`, `map` and `unwrap_or` are my favorites.
  1772. [1]: ../book/patterns.html
  1773. [2]: ../std/option/enum.Option.html#method.map
  1774. [3]: ../std/option/enum.Option.html#method.unwrap_or
  1775. [4]: ../std/option/enum.Option.html#method.unwrap_or_else
  1776. [5]: ../std/option/enum.Option.html
  1777. [6]: ../std/result/
  1778. [7]: ../std/result/enum.Result.html#method.unwrap
  1779. [8]: ../std/fmt/trait.Debug.html
  1780. [9]: ../std/primitive.str.html#method.parse
  1781. [10]: ../book/associated-types.html
  1782. [11]: https://github.com/petewarden/dstkdata
  1783. [12]: http://burntsushi.net/stuff/worldcitiespop.csv.gz
  1784. [13]: http://burntsushi.net/stuff/uscitiespop.csv.gz
  1785. [14]: http://doc.crates.io/guide.html
  1786. [15]: http://doc.rust-lang.org/getopts/getopts/index.html