@@ -14,7 +14,7 @@ When done naïvely, error handling in Rust can be verbose and annoying. This
14
14
chapter will explore those stumbling blocks and demonstrate how to use the
15
15
standard library to make error handling concise and ergonomic.
16
16
17
- ## Table of Contents
17
+ # Table of Contents
18
18
19
19
This chapter is very long, mostly because we start at the very beginning with
20
20
sum types and combinators, and try to motivate the way Rust does error handling
@@ -24,11 +24,11 @@ systems may want to jump around.
24
24
* [ The Basics] ( #the-basics )
25
25
* [ Unwrapping explained] ( #unwrapping-explained )
26
26
* [ The ` Option ` type] ( #the-option-type )
27
- * [ Composing ` Option<T> ` values] ( #composing-option-t -values )
27
+ * [ Composing ` Option<T> ` values] ( #composing-optiont -values )
28
28
* [ The ` Result ` type] ( #the-result-type )
29
29
* [ Parsing integers] ( #parsing-integers )
30
30
* [ The ` Result ` type alias idiom] ( #the-result-type-alias-idiom )
31
- * [ A brief interlude: unwrapping isn't evil] ( #a-brief-interlude-unwrapping-isn-t -evil )
31
+ * [ A brief interlude: unwrapping isn't evil] ( #a-brief-interlude-unwrapping-isnt -evil )
32
32
* [ Working with multiple error types] ( #working-with-multiple-error-types )
33
33
* [ Composing ` Option ` and ` Result ` ] ( #composing-option-and-result )
34
34
* [ The limits of combinators] ( #the-limits-of-combinators )
@@ -42,17 +42,16 @@ systems may want to jump around.
42
42
* [ Composing custom error types] ( #composing-custom-error-types )
43
43
* [ Advice for library writers] ( #advice-for-library-writers )
44
44
* [ Case study: A program to read population data] ( #case-study-a-program-to-read-population-data )
45
- * [ It's on Github] ( #it-s-on-github )
46
45
* [ Initial setup] ( #initial-setup )
47
46
* [ Argument parsing] ( #argument-parsing )
48
47
* [ Writing the logic] ( #writing-the-logic )
49
- * [ Error handling with ` Box<Error> ` ] ( #error-handling-with-box-error )
48
+ * [ Error handling with ` Box<Error> ` ] ( #error-handling-with-boxerror )
50
49
* [ Reading from stdin] ( #reading-from-stdin )
51
50
* [ Error handling with a custom type] ( #error-handling-with-a-custom-type )
52
51
* [ Adding functionality] ( #adding-functionality )
53
52
* [ The short story] ( #the-short-story )
54
53
55
- ## The Basics
54
+ # The Basics
56
55
57
56
You can think of error handling as using * case analysis* to determine whether
58
57
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
107
106
through a china shop. The bull will get to where it wants to go, but it will
108
107
trample everything in the process.
109
108
110
- ### Unwrapping explained
109
+ ## Unwrapping explained
111
110
112
111
In the previous example, we claimed
113
112
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
121
120
simple, but to do that, we will first need to explore the ` Option ` and ` Result `
122
121
types. Both of these types have a method called ` unwrap ` defined on them.
123
122
124
- ### The ` Option ` type
123
+ ## The ` Option ` type
125
124
126
125
The ` Option ` type is
127
126
[ defined in the standard library] [ 1 ] :
@@ -205,7 +204,7 @@ The `unwrap` method *abstracts away the case analysis*. This is precisely the th
205
204
that makes ` unwrap ` ergonomic to use. Unfortunately, that ` panic! ` means that
206
205
` unwrap ` is not composable: it is the bull in the china shop.
207
206
208
- #### Composing ` Option<T> ` values
207
+ ### Composing ` Option<T> ` values
209
208
210
209
In [ ` option-ex-string-find ` ] ( #code-option-ex-string-find-2 )
211
210
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
382
381
explicit case analysis. They are also composable because they permit the caller
383
382
to handle the possibility of absence in their own way. Methods like ` unwrap `
384
383
remove choices because they will panic if ` Option<T> ` is ` None ` .
385
- ### The ` Result ` type
384
+
385
+ ## The ` Result ` type
386
386
387
387
The ` Result ` type is also
388
388
[ defined in the standard library] [ 6 ] :
@@ -442,7 +442,7 @@ way to print a human readable description of values with that type.)
442
442
443
443
OK, let's move on to an example.
444
444
445
- #### Parsing integers
445
+ ### Parsing integers
446
446
447
447
The Rust standard library makes converting strings to integers dead simple.
448
448
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
548
548
combinators that affect only the error type, such as
549
549
[ ` map_err ` ] ( ../std/result/enum.Result.html#method.map_err ) (instead of
550
550
` map ` ) and [ ` or_else ` ] ( ../std/result/enum.Result.html#method.or_else )
551
- (instead of ` and_then ` ). #### The ` Result ` type alias idiom
551
+ (instead of ` and_then ` ).
552
+
553
+ ### The ` Result ` type alias idiom
552
554
553
555
In the standard library, you may frequently see types like
554
556
` Result<i32> ` . But wait, [ we defined ` Result ` ] ( #code-result-def-1 ) to
@@ -580,9 +582,7 @@ module's type alias instead of the plain definition from
580
582
` std::result ` . (This idiom is also used for
581
583
[ ` fmt::Result ` ] ( ../std/fmt/type.Result.html ) .)
582
584
583
- ### A brief interlude:
584
-
585
- unwrapping isn't evil
585
+ ## A brief interlude: unwrapping isn't evil
586
586
587
587
If you've been following along, you might have noticed that I've taken a pretty
588
588
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
620
620
explained unwrapping, let's start exploring more of the standard
621
621
library.
622
622
623
- ## Working with multiple error types
623
+ # Working with multiple error types
624
624
625
625
Thus far, we've looked at error handling where everything was either an
626
626
` Option<T> ` or a ` Result<T, SomeError> ` . 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
629
629
challenge in front of us, and it will be the major theme throughout the rest of
630
630
this chapter.
631
631
632
- ### Composing ` Option ` and ` Result `
632
+ ## Composing ` Option ` and ` Result `
633
633
634
634
So far, I've talked about combinators defined for ` Option ` and combinators
635
635
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
706
706
` Option<String> ` (from ` argv.nth(1) ` ) to a ` Result<String, String> ` , we must
707
707
also convert the ` ParseIntError ` from ` arg.parse() ` to a ` String ` .
708
708
709
- ### The limits of combinators
709
+ ## The limits of combinators
710
710
711
711
Doing IO and parsing input is a very common task, and it's one that I
712
712
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
839
839
important, but they have their limits. Let's try a different approach: early
840
840
returns.
841
841
842
- ### Early returns
842
+ ## Early returns
843
843
844
844
I'd like to take the code from the previous section and rewrite it using * early
845
845
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
886
886
back to explicit case analysis here. It turns out, there are * multiple* ways to
887
887
reduce explicit case analysis. Combinators aren't the only way.
888
888
889
- ### The ` try! ` macro
889
+ ## The ` try! ` macro
890
890
891
891
A cornerstone of error handling in Rust is the ` try! ` macro. The ` try! ` macro
892
892
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!
939
939
The bad news is that we will need to learn a bit more about a couple important
940
940
traits in the standard library before we can remove the ` map_err ` calls.
941
941
942
- ### Defining your own error type
942
+ ## Defining your own error type
943
943
944
944
Before we dive into some of the standard library error traits, I'd like to wrap
945
945
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
1033
1033
writing a library, defining your own error type should be strongly preferred so
1034
1034
that you don't remove choices from the caller unnecessarily.
1035
1035
1036
- ## Standard library traits used for error handling
1036
+ # Standard library traits used for error handling
1037
1037
1038
1038
The standard library defines two integral traits for error handling:
1039
1039
[ ` std::error::Error ` ] ( ../std/error/trait.Error.html ) and
1040
1040
[ ` std::convert::From ` ] ( ../std/convert/trait.From.html ) . While ` Error `
1041
1041
is designed specifically for generically describing errors, the ` From `
1042
1042
trait serves a more general role for converting values between two
1043
- distinct types. ### The ` Error ` trait
1043
+ distinct types.
1044
+
1045
+ ## The ` Error ` trait
1044
1046
1045
1047
The ` Error ` trait is [ defined in the standard
1046
1048
library] ( ../std/error/trait.Error.html ) :
@@ -1147,7 +1149,7 @@ We note that this is a very typical implementation of `Error`: match on your
1147
1149
different error types and satisfy the contracts defined for ` description ` and
1148
1150
` cause ` .
1149
1151
1150
- ### The ` From ` trait
1152
+ ## The ` From ` trait
1151
1153
1152
1154
The ` std::convert::From ` trait is
1153
1155
[ defined in the standard
@@ -1217,7 +1219,7 @@ us a way to reliably convert errors to the same type using the same function.
1217
1219
1218
1220
Time to revisit an old friend; the ` try! ` macro.
1219
1221
1220
- ### The real ` try! ` macro
1222
+ ## The real ` try! ` macro
1221
1223
1222
1224
Previously, we presented this definition of ` try! ` :
1223
1225
@@ -1307,7 +1309,7 @@ chapter](https://crates.io/crates/error).)
1307
1309
1308
1310
It's time to revisit our custom ` CliError ` type and tie everything together.
1309
1311
1310
- ### Composing custom error types
1312
+ ## Composing custom error types
1311
1313
1312
1314
In the last section, we looked at the real ` try! ` macro and how it does
1313
1315
automatic type conversion for us by calling ` From::from ` on the error value.
@@ -1437,7 +1439,7 @@ impl From<num::ParseFloatError> for CliError {
1437
1439
1438
1440
And that's it!
1439
1441
1440
- ### Advice for library writers
1442
+ ## Advice for library writers
1441
1443
1442
1444
If your library needs to report custom errors, then you should
1443
1445
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
1468
1470
for [ ` io::Result ` ] ( ../std/io/type.Result.html )
1469
1471
and [ ` fmt::Result ` ] ( ../std/fmt/type.Result.html ) .
1470
1472
1471
- ## Case study: A program to read population data
1473
+ # Case study: A program to read population data
1472
1474
1473
1475
This chapter was long, and depending on your background, it might be
1474
1476
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.
1492
1494
[ ` csv ` ] ( https://crates.io/crates/csv ) ,
1493
1495
and [ ` rustc-serialize ` ] ( https://crates.io/crates/rustc-serialize ) crates.
1494
1496
1495
- ### Initial setup
1497
+ ## Initial setup
1496
1498
1497
1499
We're not going to spend a lot of time on setting up a project with
1498
1500
Cargo because it is already covered well in [ the Cargo
@@ -1524,7 +1526,7 @@ cargo build --release
1524
1526
# Outputs: Hello, world!
1525
1527
```
1526
1528
1527
- ### Argument parsing
1529
+ ## Argument parsing
1528
1530
1529
1531
Let's get argument parsing out of the way. we won't go into too much
1530
1532
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
1584
1586
the help flag, we assign the proper variables to their corresponding
1585
1587
arguments.
1586
1588
1587
- ### Writing the logic
1589
+ ## Writing the logic
1588
1590
1589
1591
We're all different in how we write code, but error handling is
1590
1592
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.
1681
1683
I'd like to start with ` Box<Error> ` . Later, we'll see how defining our own
1682
1684
error type can be useful.
1683
1685
1684
- ### Error handling with ` Box<Error> `
1686
+ ## Error handling with ` Box<Error> `
1685
1687
1686
1688
` Box<Error> ` is nice because it * just works* . You don't need to define your own
1687
1689
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<Error>`, let's
1830
1832
try a different approach with our own custom error type. But first, let's take
1831
1833
a quick break from error handling and add support for reading from ` stdin ` .
1832
1834
1833
- ### Reading from stdin
1835
+ ## Reading from stdin
1834
1836
1835
1837
In our program, we accept a single file for input and do one pass over the
1836
1838
data. This means we probably should be able to accept input on stdin. But maybe
@@ -1907,7 +1909,8 @@ fn search<P: AsRef<Path>>
1907
1909
// The rest remains unchanged!
1908
1910
}
1909
1911
```
1910
- ### Error handling with a custom type
1912
+
1913
+ ## Error handling with a custom type
1911
1914
1912
1915
Previously, we learned how to
1913
1916
[ compose errors using a custom error type] ( #composing-custom-error-types ) .
@@ -2013,7 +2016,7 @@ fn search<P: AsRef<Path>>
2013
2016
2014
2017
No other changes are necessary.
2015
2018
2016
- ### Adding functionality
2019
+ ## Adding functionality
2017
2020
2018
2021
Writing generic code is great, because generalizing stuff is cool, and
2019
2022
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
2073
2076
out into the world and write your own programs and libraries with proper error
2074
2077
handling.
2075
2078
2076
- ## The Short Story
2079
+ # The Short Story
2077
2080
2078
2081
Since this chapter is long, it is useful to have a quick summary for error
2079
2082
handling in Rust. These are some good “rules of thumb." They are emphatically
0 commit comments