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