]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## Unrecoverable Errors with `panic!` |
2 | ||
3 | Sometimes, bad things happen in your code, and there’s nothing you can do about | |
923072b8 FG |
4 | it. In these cases, Rust has the `panic!` macro. There are two ways to cause a |
5 | panic in practice: by taking an action that causes our code to panic (such as | |
6 | accessing an array past the end) or by explicitly calling the `panic!` macro. | |
7 | In both cases, we cause a panic in our program. By default, these panics will | |
8 | print a failure message, unwind, clean up the stack, and quit. Via an | |
9 | environment variable, you can also have Rust display the call stack when a | |
10 | panic occurs to make it easier to track down the source of the panic. | |
13cf67c4 XL |
11 | |
12 | > ### Unwinding the Stack or Aborting in Response to a Panic | |
13 | > | |
14 | > By default, when a panic occurs, the program starts *unwinding*, which | |
15 | > means Rust walks back up the stack and cleans up the data from each function | |
5099ac24 FG |
16 | > it encounters. However, this walking back and cleanup is a lot of work. Rust, |
17 | > therefore, allows you to choose the alternative of immediately *aborting*, | |
923072b8 FG |
18 | > which ends the program without cleaning up. |
19 | > | |
20 | > Memory that the program was using will then need to be cleaned | |
21 | > up by the operating system. If in your project you need to make the resulting | |
22 | > binary as small as possible, you can switch from unwinding to aborting upon a | |
23 | > panic by adding `panic = 'abort'` to the appropriate `[profile]` sections in | |
24 | > your *Cargo.toml* file. For example, if you want to abort on panic in release | |
25 | > mode, add this: | |
13cf67c4 XL |
26 | > |
27 | > ```toml | |
28 | > [profile.release] | |
29 | > panic = 'abort' | |
30 | > ``` | |
31 | ||
32 | Let’s try calling `panic!` in a simple program: | |
33 | ||
34 | <span class="filename">Filename: src/main.rs</span> | |
35 | ||
36 | ```rust,should_panic,panics | |
74b04a01 | 37 | {{#rustdoc_include ../listings/ch09-error-handling/no-listing-01-panic/src/main.rs}} |
13cf67c4 XL |
38 | ``` |
39 | ||
40 | When you run the program, you’ll see something like this: | |
41 | ||
f035d41b | 42 | ```console |
74b04a01 | 43 | {{#include ../listings/ch09-error-handling/no-listing-01-panic/output.txt}} |
13cf67c4 XL |
44 | ``` |
45 | ||
69743fb6 XL |
46 | The call to `panic!` causes the error message contained in the last two lines. |
47 | The first line shows our panic message and the place in our source code where | |
9fa01778 XL |
48 | the panic occurred: *src/main.rs:2:5* indicates that it’s the second line, |
49 | fifth character of our *src/main.rs* file. | |
13cf67c4 XL |
50 | |
51 | In this case, the line indicated is part of our code, and if we go to that | |
52 | line, we see the `panic!` macro call. In other cases, the `panic!` call might | |
53 | be in code that our code calls, and the filename and line number reported by | |
54 | the error message will be someone else’s code where the `panic!` macro is | |
55 | called, not the line of our code that eventually led to the `panic!` call. We | |
56 | can use the backtrace of the functions the `panic!` call came from to figure | |
5099ac24 FG |
57 | out the part of our code that is causing the problem. We’ll discuss backtraces |
58 | in more detail next. | |
13cf67c4 XL |
59 | |
60 | ### Using a `panic!` Backtrace | |
61 | ||
62 | Let’s look at another example to see what it’s like when a `panic!` call comes | |
63 | from a library because of a bug in our code instead of from our code calling | |
64 | the macro directly. Listing 9-1 has some code that attempts to access an | |
5099ac24 | 65 | index in a vector beyond the range of valid indexes. |
13cf67c4 XL |
66 | |
67 | <span class="filename">Filename: src/main.rs</span> | |
68 | ||
69 | ```rust,should_panic,panics | |
74b04a01 | 70 | {{#rustdoc_include ../listings/ch09-error-handling/listing-09-01/src/main.rs}} |
13cf67c4 XL |
71 | ``` |
72 | ||
73 | <span class="caption">Listing 9-1: Attempting to access an element beyond the | |
69743fb6 | 74 | end of a vector, which will cause a call to `panic!`</span> |
13cf67c4 | 75 | |
69743fb6 | 76 | Here, we’re attempting to access the 100th element of our vector (which is at |
5099ac24 FG |
77 | index 99 because indexing starts at zero), but the vector has only 3 elements. |
78 | In this situation, Rust will panic. Using `[]` is supposed to return an | |
79 | element, but if you pass an invalid index, there’s no element that Rust could | |
80 | return here that would be correct. | |
13cf67c4 | 81 | |
74b04a01 XL |
82 | In C, attempting to read beyond the end of a data structure is undefined |
83 | behavior. You might get whatever is at the location in memory that would | |
84 | correspond to that element in the data structure, even though the memory | |
85 | doesn’t belong to that structure. This is called a *buffer overread* and can | |
86 | lead to security vulnerabilities if an attacker is able to manipulate the index | |
87 | in such a way as to read data they shouldn’t be allowed to that is stored after | |
88 | the data structure. | |
13cf67c4 XL |
89 | |
90 | To protect your program from this sort of vulnerability, if you try to read an | |
91 | element at an index that doesn’t exist, Rust will stop execution and refuse to | |
92 | continue. Let’s try it and see: | |
93 | ||
f035d41b | 94 | ```console |
74b04a01 | 95 | {{#include ../listings/ch09-error-handling/listing-09-01/output.txt}} |
13cf67c4 XL |
96 | ``` |
97 | ||
6a06907d XL |
98 | This error points at line 4 of our `main.rs` where we attempt to access index |
99 | 99. The next note line tells us that we can set the `RUST_BACKTRACE` | |
100 | environment variable to get a backtrace of exactly what happened to cause the | |
101 | error. A *backtrace* is a list of all the functions that have been called to | |
102 | get to this point. Backtraces in Rust work as they do in other languages: the | |
103 | key to reading the backtrace is to start from the top and read until you see | |
104 | files you wrote. That’s the spot where the problem originated. The lines above | |
5099ac24 FG |
105 | that spot are code that your code has called; the lines below are code that |
106 | called your code. These before-and-after lines might include core Rust code, | |
6a06907d XL |
107 | standard library code, or crates that you’re using. Let’s try getting a |
108 | backtrace by setting the `RUST_BACKTRACE` environment variable to any value | |
109 | except 0. Listing 9-2 shows output similar to what you’ll see. | |
13cf67c4 | 110 | |
74b04a01 XL |
111 | <!-- manual-regeneration |
112 | cd listings/ch09-error-handling/listing-09-01 | |
113 | RUST_BACKTRACE=1 cargo run | |
114 | copy the backtrace output below | |
115 | check the backtrace number mentioned in the text below the listing | |
116 | --> | |
117 | ||
f035d41b | 118 | ```console |
13cf67c4 | 119 | $ RUST_BACKTRACE=1 cargo run |
c620b35d FG |
120 | thread 'main' panicked at src/main.rs:4:6: |
121 | index out of bounds: the len is 3 but the index is 99 | |
13cf67c4 | 122 | stack backtrace: |
fc512014 | 123 | 0: rust_begin_unwind |
c620b35d | 124 | at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5 |
fc512014 | 125 | 1: core::panicking::panic_fmt |
c620b35d | 126 | at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14 |
fc512014 | 127 | 2: core::panicking::panic_bounds_check |
c620b35d | 128 | at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:208:5 |
fc512014 | 129 | 3: <usize as core::slice::index::SliceIndex<[T]>>::index |
c620b35d | 130 | at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/slice/index.rs:255:10 |
fc512014 | 131 | 4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index |
c620b35d | 132 | at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/slice/index.rs:18:9 |
2b03887a | 133 | 5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index |
c620b35d | 134 | at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/alloc/src/vec/mod.rs:2770:9 |
fc512014 | 135 | 6: panic::main |
c620b35d | 136 | at ./src/main.rs:4:6 |
fc512014 | 137 | 7: core::ops::function::FnOnce::call_once |
c620b35d | 138 | at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5 |
fc512014 | 139 | note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. |
13cf67c4 XL |
140 | ``` |
141 | ||
142 | <span class="caption">Listing 9-2: The backtrace generated by a call to | |
143 | `panic!` displayed when the environment variable `RUST_BACKTRACE` is set</span> | |
144 | ||
145 | That’s a lot of output! The exact output you see might be different depending | |
146 | on your operating system and Rust version. In order to get backtraces with this | |
147 | information, debug symbols must be enabled. Debug symbols are enabled by | |
148 | default when using `cargo build` or `cargo run` without the `--release` flag, | |
149 | as we have here. | |
150 | ||
5099ac24 FG |
151 | In the output in Listing 9-2, line 6 of the backtrace points to the line in our |
152 | project that’s causing the problem: line 4 of *src/main.rs*. If we don’t want | |
153 | our program to panic, we should start our investigation at the location pointed | |
154 | to by the first line mentioning a file we wrote. In Listing 9-1, where we | |
155 | deliberately wrote code that would panic, the way to fix the panic is to not | |
156 | request an element beyond the range of the vector indexes. When your code | |
157 | panics in the future, you’ll need to figure out what action the code is taking | |
158 | with what values to cause the panic and what the code should do instead. | |
13cf67c4 XL |
159 | |
160 | We’ll come back to `panic!` and when we should and should not use `panic!` to | |
dc9dc135 XL |
161 | handle error conditions in the [“To `panic!` or Not to |
162 | `panic!`”][to-panic-or-not-to-panic]<!-- ignore --> section later in this | |
163 | chapter. Next, we’ll look at how to recover from an error using `Result`. | |
9fa01778 XL |
164 | |
165 | [to-panic-or-not-to-panic]: | |
166 | ch09-03-to-panic-or-not-to-panic.html#to-panic-or-not-to-panic |