]> git.proxmox.com Git - rustc.git/blob - src/doc/trpl/error-handling.md
Imported Upstream version 1.1.0+dfsg1
[rustc.git] / src / doc / trpl / error-handling.md
1 % Error Handling
2
3 > The best-laid plans of mice and men
4 > Often go awry
5 >
6 > "Tae a Moose", Robert Burns
7
8 Sometimes, things just go wrong. It's important to have a plan for when the
9 inevitable happens. Rust has rich support for handling errors that may (let's
10 be honest: will) occur in your programs.
11
12 There are two main kinds of errors that can occur in your programs: failures,
13 and panics. Let's talk about the difference between the two, and then discuss
14 how to handle each. Then, we'll discuss upgrading failures to panics.
15
16 # Failure vs. Panic
17
18 Rust uses two terms to differentiate between two forms of error: failure, and
19 panic. A *failure* is an error that can be recovered from in some way. A
20 *panic* is an error that cannot be recovered from.
21
22 What do we mean by "recover"? Well, in most cases, the possibility of an error
23 is expected. For example, consider the `parse` function:
24
25 ```ignore
26 "5".parse();
27 ```
28
29 This method converts a string into another type. But because it's a string, you
30 can't be sure that the conversion actually works. For example, what should this
31 convert to?
32
33 ```ignore
34 "hello5world".parse();
35 ```
36
37 This won't work. So we know that this function will only work properly for some
38 inputs. It's expected behavior. We call this kind of error a *failure*.
39
40 On the other hand, sometimes, there are errors that are unexpected, or which
41 we cannot recover from. A classic example is an `assert!`:
42
43 ```rust
44 # let x = 5;
45 assert!(x == 5);
46 ```
47
48 We use `assert!` to declare that something is true. If it's not true, something
49 is very wrong. Wrong enough that we can't continue with things in the current
50 state. Another example is using the `unreachable!()` macro:
51
52 ```{rust,ignore}
53 enum Event {
54 NewRelease,
55 }
56
57 fn probability(_: &Event) -> f64 {
58 // real implementation would be more complex, of course
59 0.95
60 }
61
62 fn descriptive_probability(event: Event) -> &'static str {
63 match probability(&event) {
64 1.00 => "certain",
65 0.00 => "impossible",
66 0.00 ... 0.25 => "very unlikely",
67 0.25 ... 0.50 => "unlikely",
68 0.50 ... 0.75 => "likely",
69 0.75 ... 1.00 => "very likely",
70 }
71 }
72
73 fn main() {
74 std::io::println(descriptive_probability(NewRelease));
75 }
76 ```
77
78 This will give us an error:
79
80 ```text
81 error: non-exhaustive patterns: `_` not covered [E0004]
82 ```
83
84 While we know that we've covered all possible cases, Rust can't tell. It
85 doesn't know that probability is between 0.0 and 1.0. So we add another case:
86
87 ```rust
88 use Event::NewRelease;
89
90 enum Event {
91 NewRelease,
92 }
93
94 fn probability(_: &Event) -> f64 {
95 // real implementation would be more complex, of course
96 0.95
97 }
98
99 fn descriptive_probability(event: Event) -> &'static str {
100 match probability(&event) {
101 1.00 => "certain",
102 0.00 => "impossible",
103 0.00 ... 0.25 => "very unlikely",
104 0.25 ... 0.50 => "unlikely",
105 0.50 ... 0.75 => "likely",
106 0.75 ... 1.00 => "very likely",
107 _ => unreachable!()
108 }
109 }
110
111 fn main() {
112 println!("{}", descriptive_probability(NewRelease));
113 }
114 ```
115
116 We shouldn't ever hit the `_` case, so we use the `unreachable!()` macro to
117 indicate this. `unreachable!()` gives a different kind of error than `Result`.
118 Rust calls these sorts of errors *panics*.
119
120 # Handling errors with `Option` and `Result`
121
122 The simplest way to indicate that a function may fail is to use the `Option<T>`
123 type. For example, the `find` method on strings attempts to find a pattern
124 in a string, and returns an `Option`:
125
126 ```rust
127 let s = "foo";
128
129 assert_eq!(s.find('f'), Some(0));
130 assert_eq!(s.find('z'), None);
131 ```
132
133
134 This is appropriate for the simplest of cases, but doesn't give us a lot of
135 information in the failure case. What if we wanted to know _why_ the function
136 failed? For this, we can use the `Result<T, E>` type. It looks like this:
137
138 ```rust
139 enum Result<T, E> {
140 Ok(T),
141 Err(E)
142 }
143 ```
144
145 This enum is provided by Rust itself, so you don't need to define it to use it
146 in your code. The `Ok(T)` variant represents a success, and the `Err(E)` variant
147 represents a failure. Returning a `Result` instead of an `Option` is recommended
148 for all but the most trivial of situations.
149
150 Here's an example of using `Result`:
151
152 ```rust
153 #[derive(Debug)]
154 enum Version { Version1, Version2 }
155
156 #[derive(Debug)]
157 enum ParseError { InvalidHeaderLength, InvalidVersion }
158
159 fn parse_version(header: &[u8]) -> Result<Version, ParseError> {
160 if header.len() < 1 {
161 return Err(ParseError::InvalidHeaderLength);
162 }
163 match header[0] {
164 1 => Ok(Version::Version1),
165 2 => Ok(Version::Version2),
166 _ => Err(ParseError::InvalidVersion)
167 }
168 }
169
170 let version = parse_version(&[1, 2, 3, 4]);
171 match version {
172 Ok(v) => {
173 println!("working with version: {:?}", v);
174 }
175 Err(e) => {
176 println!("error parsing header: {:?}", e);
177 }
178 }
179 ```
180
181 This function makes use of an enum, `ParseError`, to enumerate the various
182 errors that can occur.
183
184 The [`Debug`](../std/fmt/trait.Debug.html) trait is what lets us print the enum value using the `{:?}` format operation.
185
186 # Non-recoverable errors with `panic!`
187
188 In the case of an error that is unexpected and not recoverable, the `panic!`
189 macro will induce a panic. This will crash the current thread, and give an error:
190
191 ```{rust,ignore}
192 panic!("boom");
193 ```
194
195 gives
196
197 ```text
198 thread '<main>' panicked at 'boom', hello.rs:2
199 ```
200
201 when you run it.
202
203 Because these kinds of situations are relatively rare, use panics sparingly.
204
205 # Upgrading failures to panics
206
207 In certain circumstances, even though a function may fail, we may want to treat
208 it as a panic instead. For example, `io::stdin().read_line(&mut buffer)` returns
209 a `Result<usize>`, when there is an error reading the line. This allows us to
210 handle and possibly recover from error.
211
212 If we don't want to handle this error, and would rather just abort the program,
213 we can use the `unwrap()` method:
214
215 ```{rust,ignore}
216 io::stdin().read_line(&mut buffer).unwrap();
217 ```
218
219 `unwrap()` will `panic!` if the `Result` is `Err`. This basically says "Give
220 me the value, and if something goes wrong, just crash." This is less reliable
221 than matching the error and attempting to recover, but is also significantly
222 shorter. Sometimes, just crashing is appropriate.
223
224 There's another way of doing this that's a bit nicer than `unwrap()`:
225
226 ```{rust,ignore}
227 let mut buffer = String::new();
228 let input = io::stdin().read_line(&mut buffer)
229 .ok()
230 .expect("Failed to read line");
231 ```
232
233 `ok()` converts the `Result` into an `Option`, and `expect()` does the same
234 thing as `unwrap()`, but takes a message. This message is passed along to the
235 underlying `panic!`, providing a better error message if the code errors.
236
237 # Using `try!`
238
239 When writing code that calls many functions that return the `Result` type, the
240 error handling can be tedious. The `try!` macro hides some of the boilerplate
241 of propagating errors up the call stack.
242
243 It replaces this:
244
245 ```rust
246 use std::fs::File;
247 use std::io;
248 use std::io::prelude::*;
249
250 struct Info {
251 name: String,
252 age: i32,
253 rating: i32,
254 }
255
256 fn write_info(info: &Info) -> io::Result<()> {
257 let mut file = File::create("my_best_friends.txt").unwrap();
258
259 if let Err(e) = writeln!(&mut file, "name: {}", info.name) {
260 return Err(e)
261 }
262 if let Err(e) = writeln!(&mut file, "age: {}", info.age) {
263 return Err(e)
264 }
265 if let Err(e) = writeln!(&mut file, "rating: {}", info.rating) {
266 return Err(e)
267 }
268
269 return Ok(());
270 }
271 ```
272
273 With this:
274
275 ```rust
276 use std::fs::File;
277 use std::io;
278 use std::io::prelude::*;
279
280 struct Info {
281 name: String,
282 age: i32,
283 rating: i32,
284 }
285
286 fn write_info(info: &Info) -> io::Result<()> {
287 let mut file = try!(File::create("my_best_friends.txt"));
288
289 try!(writeln!(&mut file, "name: {}", info.name));
290 try!(writeln!(&mut file, "age: {}", info.age));
291 try!(writeln!(&mut file, "rating: {}", info.rating));
292
293 return Ok(());
294 }
295 ```
296
297 Wrapping an expression in `try!` will result in the unwrapped success (`Ok`)
298 value, unless the result is `Err`, in which case `Err` is returned early from
299 the enclosing function.
300
301 It's worth noting that you can only use `try!` from a function that returns a
302 `Result`, which means that you cannot use `try!` inside of `main()`, because
303 `main()` doesn't return anything.
304
305 `try!` makes use of [`From<Error>`](../std/convert/trait.From.html) to determine
306 what to return in the error case.