diff --git a/src/doc/trpl/error-handling.md b/src/doc/trpl/error-handling.md index 51343f925c7c..673dc950ecce 100644 --- a/src/doc/trpl/error-handling.md +++ b/src/doc/trpl/error-handling.md @@ -14,7 +14,7 @@ When done naïvely, error handling in Rust can be verbose and annoying. This chapter will explore those stumbling blocks and demonstrate how to use the standard library to make error handling concise and ergonomic. -## Table of Contents +# Table of Contents This chapter is very long, mostly because we start at the very beginning with sum types and combinators, and try to motivate the way Rust does error handling @@ -24,11 +24,11 @@ systems may want to jump around. * [The Basics](#the-basics) * [Unwrapping explained](#unwrapping-explained) * [The `Option` type](#the-option-type) - * [Composing `Option` values](#composing-option-t-values) + * [Composing `Option` values](#composing-optiont-values) * [The `Result` type](#the-result-type) * [Parsing integers](#parsing-integers) * [The `Result` type alias idiom](#the-result-type-alias-idiom) - * [A brief interlude: unwrapping isn't evil](#a-brief-interlude-unwrapping-isn-t-evil) + * [A brief interlude: unwrapping isn't evil](#a-brief-interlude-unwrapping-isnt-evil) * [Working with multiple error types](#working-with-multiple-error-types) * [Composing `Option` and `Result`](#composing-option-and-result) * [The limits of combinators](#the-limits-of-combinators) @@ -42,17 +42,16 @@ systems may want to jump around. * [Composing custom error types](#composing-custom-error-types) * [Advice for library writers](#advice-for-library-writers) * [Case study: A program to read population data](#case-study-a-program-to-read-population-data) - * [It's on Github](#it-s-on-github) * [Initial setup](#initial-setup) * [Argument parsing](#argument-parsing) * [Writing the logic](#writing-the-logic) - * [Error handling with `Box`](#error-handling-with-box-error) + * [Error handling with `Box`](#error-handling-with-boxerror) * [Reading from stdin](#reading-from-stdin) * [Error handling with a custom type](#error-handling-with-a-custom-type) * [Adding functionality](#adding-functionality) * [The short story](#the-short-story) -## The Basics +# The Basics You can think of error handling as using *case analysis* to determine whether a computation was successful or not. As you will see, the key to ergonomic error @@ -107,7 +106,7 @@ You can think of this style of error handling as similar to a bull running through a china shop. The bull will get to where it wants to go, but it will trample everything in the process. -### Unwrapping explained +## Unwrapping explained In the previous example, we claimed that the program would simply panic if it reached one of the two error @@ -121,7 +120,7 @@ It would be better if we just showed the code for unwrapping because it is so simple, but to do that, we will first need to explore the `Option` and `Result` types. Both of these types have a method called `unwrap` defined on them. -### The `Option` type +## The `Option` type The `Option` type is [defined in the standard library][1]: @@ -205,7 +204,7 @@ The `unwrap` method *abstracts away the case analysis*. This is precisely the th that makes `unwrap` ergonomic to use. Unfortunately, that `panic!` means that `unwrap` is not composable: it is the bull in the china shop. -#### Composing `Option` values +### Composing `Option` values In [`option-ex-string-find`](#code-option-ex-string-find-2) we saw how to use `find` to discover the extension in a file name. Of course, @@ -382,7 +381,8 @@ Combinators make using types like `Option` ergonomic because they reduce explicit case analysis. They are also composable because they permit the caller to handle the possibility of absence in their own way. Methods like `unwrap` remove choices because they will panic if `Option` is `None`. -### The `Result` type + +## The `Result` type The `Result` type is also [defined in the standard library][6]: @@ -442,7 +442,7 @@ way to print a human readable description of values with that type.) OK, let's move on to an example. -#### Parsing integers +### Parsing integers The Rust standard library makes converting strings to integers dead simple. It's so easy in fact, that it is very tempting to write something like the @@ -548,7 +548,9 @@ Additionally, since `Result` has a second type parameter, there are combinators that affect only the error type, such as [`map_err`](../std/result/enum.Result.html#method.map_err) (instead of `map`) and [`or_else`](../std/result/enum.Result.html#method.or_else) -(instead of `and_then`). #### The `Result` type alias idiom +(instead of `and_then`). + +### The `Result` type alias idiom In the standard library, you may frequently see types like `Result`. But wait, [we defined `Result`](#code-result-def-1) to @@ -580,9 +582,7 @@ module's type alias instead of the plain definition from `std::result`. (This idiom is also used for [`fmt::Result`](../std/fmt/type.Result.html).) -### A brief interlude: - -unwrapping isn't evil +## A brief interlude: unwrapping isn't evil If you've been following along, you might have noticed that I've taken a pretty hard line against calling methods like `unwrap` that could `panic` and abort @@ -620,7 +620,7 @@ Now that we've covered the basics of error handling in Rust, and explained unwrapping, let's start exploring more of the standard library. -## Working with multiple error types +# Working with multiple error types Thus far, we've looked at error handling where everything was either an `Option` or a `Result`. But what happens when you have both an @@ -629,7 +629,7 @@ Thus far, we've looked at error handling where everything was either an challenge in front of us, and it will be the major theme throughout the rest of this chapter. -### Composing `Option` and `Result` +## Composing `Option` and `Result` So far, I've talked about combinators defined for `Option` and combinators defined for `Result`. We can use these combinators to compose results of @@ -706,7 +706,7 @@ the same (because of our use of `and_then`). Since we chose to convert the `Option` (from `argv.nth(1)`) to a `Result`, we must also convert the `ParseIntError` from `arg.parse()` to a `String`. -### The limits of combinators +## The limits of combinators Doing IO and parsing input is a very common task, and it's one that I personally have done a lot of in Rust. Therefore, we will use (and continue to @@ -839,7 +839,7 @@ With all of that said, the code is still hairy. Mastering use of combinators is important, but they have their limits. Let's try a different approach: early returns. -### Early returns +## Early returns I'd like to take the code from the previous section and rewrite it using *early returns*. Early returns let you exit the function early. We can't return early @@ -886,7 +886,7 @@ ergonomic error handling is reducing explicit case analysis, yet we've reverted back to explicit case analysis here. It turns out, there are *multiple* ways to reduce explicit case analysis. Combinators aren't the only way. -### The `try!` macro +## The `try!` macro A cornerstone of error handling in Rust is the `try!` macro. The `try!` macro abstracts case analysis just like combinators, but unlike combinators, it also @@ -939,7 +939,7 @@ The good news is that we will soon learn how to remove those `map_err` calls! The bad news is that we will need to learn a bit more about a couple important traits in the standard library before we can remove the `map_err` calls. -### Defining your own error type +## Defining your own error type Before we dive into some of the standard library error traits, I'd like to wrap up this section by removing the use of `String` as our error type in the @@ -1033,14 +1033,16 @@ will do in a pinch, particularly if you're writing an application. If you're writing a library, defining your own error type should be strongly preferred so that you don't remove choices from the caller unnecessarily. -## Standard library traits used for error handling +# Standard library traits used for error handling The standard library defines two integral traits for error handling: [`std::error::Error`](../std/error/trait.Error.html) and [`std::convert::From`](../std/convert/trait.From.html). While `Error` is designed specifically for generically describing errors, the `From` trait serves a more general role for converting values between two -distinct types. ### The `Error` trait +distinct types. + +## The `Error` trait The `Error` trait is [defined in the standard library](../std/error/trait.Error.html): @@ -1147,7 +1149,7 @@ We note that this is a very typical implementation of `Error`: match on your different error types and satisfy the contracts defined for `description` and `cause`. -### The `From` trait +## The `From` trait The `std::convert::From` trait is [defined in the standard @@ -1217,7 +1219,7 @@ us a way to reliably convert errors to the same type using the same function. Time to revisit an old friend; the `try!` macro. -### The real `try!` macro +## The real `try!` macro Previously, we presented this definition of `try!`: @@ -1307,7 +1309,7 @@ chapter](https://crates.io/crates/error).) It's time to revisit our custom `CliError` type and tie everything together. -### Composing custom error types +## Composing custom error types In the last section, we looked at the real `try!` macro and how it does automatic type conversion for us by calling `From::from` on the error value. @@ -1437,7 +1439,7 @@ impl From for CliError { And that's it! -### Advice for library writers +## Advice for library writers If your library needs to report custom errors, then you should probably define your own error type. It's up to you whether or not to @@ -1468,7 +1470,7 @@ library defines a single error type. This is used in the standard library for [`io::Result`](../std/io/type.Result.html) and [`fmt::Result`](../std/fmt/type.Result.html). -## Case study: A program to read population data +# Case study: A program to read population data This chapter was long, and depending on your background, it might be rather dense. While there is plenty of example code to go along with @@ -1492,7 +1494,7 @@ parse the program arguments and decode that stuff into Rust types automatically. [`csv`](https://crates.io/crates/csv), and [`rustc-serialize`](https://crates.io/crates/rustc-serialize) crates. -### Initial setup +## Initial setup We're not going to spend a lot of time on setting up a project with Cargo because it is already covered well in [the Cargo @@ -1524,7 +1526,7 @@ cargo build --release # Outputs: Hello, world! ``` -### Argument parsing +## Argument parsing Let's get argument parsing out of the way. we won't go into too much detail on Getopts, but there is [some good documentation][15] @@ -1584,7 +1586,7 @@ print for the program name and template. If the user has not passed in the help flag, we assign the proper variables to their corresponding arguments. -### Writing the logic +## Writing the logic We're all different in how we write code, but error handling is usually the last thing we want to think about. This isn't very good @@ -1681,7 +1683,7 @@ explore two different ways to approach handling these errors. I'd like to start with `Box`. Later, we'll see how defining our own error type can be useful. -### Error handling with `Box` +## Error handling with `Box` `Box` is nice because it *just works*. You don't need to define your own error types and you don't need any `From` implementations. The downside is that @@ -1830,7 +1832,7 @@ Now that we've seen how to do proper error handling with `Box`, let's try a different approach with our own custom error type. But first, let's take a quick break from error handling and add support for reading from `stdin`. -### Reading from stdin +## Reading from stdin In our program, we accept a single file for input and do one pass over the data. This means we probably should be able to accept input on stdin. But maybe @@ -1907,7 +1909,8 @@ fn search> // The rest remains unchanged! } ``` -### Error handling with a custom type + +## Error handling with a custom type Previously, we learned how to [compose errors using a custom error type](#composing-custom-error-types). @@ -2013,7 +2016,7 @@ fn search> No other changes are necessary. -### Adding functionality +## Adding functionality Writing generic code is great, because generalizing stuff is cool, and it can then be useful later. But sometimes, the juice isn't worth the @@ -2073,7 +2076,7 @@ This pretty much sums up our case study. From here, you should be ready to go out into the world and write your own programs and libraries with proper error handling. -## The Short Story +# The Short Story Since this chapter is long, it is useful to have a quick summary for error handling in Rust. These are some good “rules of thumb." They are emphatically