Skip to content

Commit 146c07b

Browse files
committed
error handling take two
1 parent caceeac commit 146c07b

4 files changed

+305
-117
lines changed

src/ch07-01-error-handling.md

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,13 @@ Errors are a fact of life in software. Rust has a number of tools that you can
55
use to handle things when something bad happens.
66

77
Rust splits errors into two major kinds: errors that are recoverable, and
8-
errors that are not recoverable. It has two different strategies to handle
9-
these two different forms of errors.
10-
11-
What does it mean to "recover" from an error? In the simplest sense, it
12-
relates to the answer of this question:
8+
errors that are not recoverable. What does it mean to "recover" from an
9+
error? In the simplest sense, it relates to the answer of this question:
1310

1411
> If I call a function, and something bad happens, can I do something
1512
> meaningful? Or should execution stop?
1613
17-
Some kinds of problems have solutions, but with other kinds of errors,
18-
all you can do is throw up your hands and give up.
19-
20-
We'll start off our examination of error handling by talking about the
21-
unrecoverable case first. Why? You can think of unrecoverable errors as a
22-
subset of recoverable errors. If you choose to treat an error as unrecoverable,
23-
then the function that calls your code has no choice in the matter. However, if
24-
your function returns a recoverable error, the calling function has a choice:
25-
handle the error properly, or convert it into an unrecoverable error. This is
26-
one of the reasons that most Rust code chooses to treat errors as recoverable:
27-
it's more flexible. We're going to explore an example that starts off as
28-
unrecoverable, and then, in the next section, we will convert it to a
29-
convertable error. Finally, we'll talk about how to create our own custom error
30-
types.
14+
The technique that you use depends on the answer to this question. First,
15+
we'll talk about `panic!`, Rust's way of signaling an unrecoverable error.
16+
Then, we'll talk about `Result<T, E>`, the return type for functions that
17+
may return an error, but one you can recover from.
Lines changed: 130 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,143 @@
11
# Unrecoverable errors with panic!
22

3-
You've already seen the way to signal an unrecoverable error: the `panic!`
4-
macro. Here's an example of using `panic!`:
3+
Sometimes, bad things happen, and there's nothing that you can do about it. For
4+
these cases, Rust has a macro, `panic!`. When this macro executes, your program
5+
will terminate execution, printing a failure message and then quitting. Try
6+
this program:
57

6-
```rust
7-
fn check_guess(number: u32) -> bool {
8-
if number > 100 {
9-
panic!("Guess was too big: {}", number);
10-
}
11-
12-
number == 34
8+
```rust,should_panic
9+
fn main() {
10+
panic!("crash and burn");
1311
}
1412
```
1513

16-
This function accepts a guess between zero and a hundred, and checks if it's
17-
equivalent to the correct number, which is `34` in this case. It's kind of a
18-
silly function, but it's similar to a real example you've already seen:
19-
indexing vectors:
14+
If you run it, you'll see something like this:
15+
16+
```bash
17+
$ cargo run
18+
Compiling panic v0.1.0 (file:///home/steve/tmp/panic)
19+
Finished debug [unoptimized + debuginfo] target(s) in 0.25 secs
20+
Running `target/debug/panic`
21+
thread 'main' panicked at 'crash and burn', src/main.rs:2
22+
note: Run with `RUST_BACKTRACE=1` for a backtrace.
23+
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
24+
```
25+
26+
There are three lines of error message here. The first line shows our panic
27+
message and the place in our source code where the panic occurred:
28+
`src/main.rs`, line two.
29+
30+
But that only shows us the exact line that called `panic!`. That's not always
31+
useful. Let's modify our example slightly:
2032
2133
```rust,should_panic
22-
let v = vec![1, 2, 3];
34+
fn main() {
35+
let v = vec![1, 2, 3];
36+
37+
v[100];
38+
}
39+
```
40+
41+
We attempt to access the hundredth element of our vector, but it only has three
42+
elements. In this situation, Rust will panic. Let's try it:
43+
44+
```bash
45+
$ cargo run
46+
Compiling panic v0.1.0 (file:///home/steve/tmp/panic)
47+
Finished debug [unoptimized + debuginfo] target(s) in 0.27 secs
48+
Running `target/debug/panic`
49+
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
50+
100', ../src/libcollections/vec.rs:1265
51+
note: Run with `RUST_BACKTRACE=1` for a backtrace.
52+
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
53+
```
2354
24-
v[1000]; // this will panic
55+
This points at a file we didn't write, `../src/libcollections/vec.rs`, line 1265.
56+
That's the implementation of `Vec<T>` in the standard library. While it's easy
57+
to see in this short program where the error was, it would be nicer if we could
58+
have Rust tell us what line in our program caused the error.
59+
60+
That's what the next line, the `note` is about. If we set the `RUST_BACKTRACE`
61+
environment variable, we'll get a backtrace of exactly how the error happend.
62+
Let's try it:
63+
64+
```bash
65+
$ RUST_BACKTRACE=1 cargo run
66+
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
67+
Running `target/debug/panic`
68+
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
69+
100', ../src/libcollections/vec.rs:1265
70+
stack backtrace:
71+
1: 0x560956150ae9 -
72+
std::sys::backtrace::tracing::imp::write::h482d45d91246faa2
73+
2: 0x56095615345c -
74+
std::panicking::default_hook::_{{closure}}::h89158f66286b674e
75+
3: 0x56095615291e - std::panicking::default_hook::h9e30d428ee3b0c43
76+
4: 0x560956152f88 -
77+
std::panicking::rust_panic_with_hook::h2224f33fb7bf2f4c
78+
5: 0x560956152e22 - std::panicking::begin_panic::hcb11a4dc6d779ae5
79+
6: 0x560956152d50 - std::panicking::begin_panic_fmt::h310416c62f3935b3
80+
7: 0x560956152cd1 - rust_begin_unwind
81+
8: 0x560956188a2f - core::panicking::panic_fmt::hc5789f4e80194729
82+
9: 0x5609561889d3 -
83+
core::panicking::panic_bounds_check::hb2d969c3cc11ed08
84+
10: 0x56095614c075 - _<collections..vec..Vec<T> as
85+
core..ops..Index<usize>>::index::hb9f10d3dadbe8101
86+
at ../src/libcollections/vec.rs:1265
87+
11: 0x56095614c134 - panic::main::h2d7d3751fb8705e2
88+
at /home/steve/tmp/panic/src/main.rs:4
89+
12: 0x56095615af46 - __rust_maybe_catch_panic
90+
13: 0x560956152082 - std::rt::lang_start::h352a66f5026f54bd
91+
14: 0x56095614c1b3 - main
92+
15: 0x7f75b88ed72f - __libc_start_main
93+
16: 0x56095614b3c8 - _start
94+
17: 0x0 - <unknown>
95+
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
2596
```
2697
27-
The implementation of indexing with `[]` looks similar to our `check_guess`
28-
function above: check if the number is larger than the length of the vector,
29-
and if it is, panic.
98+
That's a lot of output! Line `11` there has the line in our project:
99+
`src/main.rs` line four. We've been looking at the error message, but Cargo
100+
also told us something important about backtraces early on: `[unoptimized +
101+
debuginfo]`. 'debuginfo' is what enables the file names to be shown here.
102+
If we instead compile with `--release`:
103+
104+
```bash
105+
$ RUST_BACKTRACE=1 cargo run --release
106+
Compiling panic v0.1.0 (file:///home/steve/tmp/panic)
107+
Finished release [optimized] target(s) in 0.28 secs
108+
Running `target/release/panic`
109+
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
110+
100', ../src/libcollections/vec.rs:1265
111+
stack backtrace:
112+
1: 0x565238fd0e79 -
113+
std::sys::backtrace::tracing::imp::write::h482d45d91246faa2
114+
2: 0x565238fd37ec -
115+
std::panicking::default_hook::_{{closure}}::h89158f66286b674e
116+
3: 0x565238fd2cae - std::panicking::default_hook::h9e30d428ee3b0c43
117+
4: 0x565238fd3318 -
118+
std::panicking::rust_panic_with_hook::h2224f33fb7bf2f4c
119+
5: 0x565238fd31b2 - std::panicking::begin_panic::hcb11a4dc6d779ae5
120+
6: 0x565238fd30e0 - std::panicking::begin_panic_fmt::h310416c62f3935b3
121+
7: 0x565238fd3061 - rust_begin_unwind
122+
8: 0x565239008dbf - core::panicking::panic_fmt::hc5789f4e80194729
123+
9: 0x565239008d63 -
124+
core::panicking::panic_bounds_check::hb2d969c3cc11ed08
125+
10: 0x565238fcc526 - panic::main::h2d7d3751fb8705e2
126+
11: 0x565238fdb2d6 - __rust_maybe_catch_panic
127+
12: 0x565238fd2412 - std::rt::lang_start::h352a66f5026f54bd
128+
13: 0x7f36aad6372f - __libc_start_main
129+
14: 0x565238fcc408 - _start
130+
15: 0x0 - <unknown>
131+
error: Process didn't exit successfully: `target/release/panic` (exit code:
132+
101)
133+
```
30134
31-
Why do we need to panic? There's no number type for "between zero and a
32-
hundred" in Rust, so we are accepting a `u32`, and then checking in the
33-
function's body to make sure the guess is in-bounds. If the number is too big,
34-
there's been an error: someone made a mistake. We then invoke `panic!` to say
35-
"something went wrong, we cannot continue to run this program."
135+
Now it just says 'optimized', and we don't have the file names any more. These
136+
settings are only the default; you can include debuginfo in a release build,
137+
or exclude it from a debug build, by configuring Cargo. See its documentation
138+
for more details: http://doc.crates.io/manifest.html#the-profile-sections
36139
140+
So why does Rust panic here? In this case, using `[]` is supposed to return
141+
a number. But if you pass it an invalid index, there's no number Rust could
142+
return here, it would be wrong. So the only thing that we can do is terminate
143+
the program.

0 commit comments

Comments
 (0)