164 lines
10 KiB
HTML
164 lines
10 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Unrecoverable Errors with panic!</title>
|
||
</head>
|
||
<body>
|
||
<h2 id="unrecoverable-errors-with-panic"><a class="header" href="#unrecoverable-errors-with-panic">Unrecoverable Errors with <code>panic!</code></a></h2>
|
||
<p>Sometimes bad things happen in your code, and there’s nothing you can do about
|
||
it. In these cases, Rust has the <code>panic!</code> macro. There are two ways to cause a
|
||
panic in practice: by taking an action that causes our code to panic (such as
|
||
accessing an array past the end) or by explicitly calling the <code>panic!</code> macro.
|
||
In both cases, we cause a panic in our program. By default, these panics will
|
||
print a failure message, unwind, clean up the stack, and quit. Via an
|
||
environment variable, you can also have Rust display the call stack when a
|
||
panic occurs to make it easier to track down the source of the panic.</p>
|
||
<section class="note" aria-role="note">
|
||
<h3 id="unwinding-the-stack-or-aborting-in-response-to-a-panic"><a class="header" href="#unwinding-the-stack-or-aborting-in-response-to-a-panic">Unwinding the Stack or Aborting in Response to a Panic</a></h3>
|
||
<p>By default, when a panic occurs, the program starts <em>unwinding</em>, which means
|
||
Rust walks back up the stack and cleans up the data from each function it
|
||
encounters. However, walking back and cleaning up is a lot of work. Rust
|
||
therefore allows you to choose the alternative of immediately <em>aborting</em>,
|
||
which ends the program without cleaning up.</p>
|
||
<p>Memory that the program was using will then need to be cleaned up by the
|
||
operating system. If in your project you need to make the resultant binary as
|
||
small as possible, you can switch from unwinding to aborting upon a panic by
|
||
adding <code>panic = 'abort'</code> to the appropriate <code>[profile]</code> sections in your
|
||
<em>Cargo.toml</em> file. For example, if you want to abort on panic in release mode,
|
||
add this:</p>
|
||
<pre><code class="language-toml">[profile.release]
|
||
panic = 'abort'
|
||
</code></pre>
|
||
</section>
|
||
<p>Let’s try calling <code>panic!</code> in a simple program:</p>
|
||
<figure class="listing">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust should_panic panics edition2024">fn main() {
|
||
panic!("crash and burn");
|
||
}</code></pre>
|
||
</figure>
|
||
<p>When you run the program, you’ll see something like this:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling panic v0.1.0 (file:///projects/panic)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s
|
||
Running `target/debug/panic`
|
||
|
||
thread 'main' panicked at src/main.rs:2:5:
|
||
crash and burn
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
</code></pre>
|
||
<p>The call to <code>panic!</code> causes the error message contained in the last two lines.
|
||
The first line shows our panic message and the place in our source code where
|
||
the panic occurred: <em>src/main.rs:2:5</em> indicates that it’s the second line,
|
||
fifth character of our <em>src/main.rs</em> file.</p>
|
||
<p>In this case, the line indicated is part of our code, and if we go to that
|
||
line, we see the <code>panic!</code> macro call. In other cases, the <code>panic!</code> call might
|
||
be in code that our code calls, and the filename and line number reported by
|
||
the error message will be someone else’s code where the <code>panic!</code> macro is
|
||
called, not the line of our code that eventually led to the <code>panic!</code> call.</p>
|
||
<!-- Old headings. Do not remove or links may break. -->
|
||
<p><a id="using-a-panic-backtrace"></a></p>
|
||
<p>We can use the backtrace of the functions the <code>panic!</code> call came from to figure
|
||
out the part of our code that is causing the problem. To understand how to use
|
||
a <code>panic!</code> backtrace, let’s look at another example and see what it’s like when
|
||
a <code>panic!</code> call comes from a library because of a bug in our code instead of
|
||
from our code calling the macro directly. Listing 9-1 has some code that
|
||
attempts to access an index in a vector beyond the range of valid indexes.</p>
|
||
<figure class="listing" id="listing-9-1">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust should_panic panics edition2024">fn main() {
|
||
let v = vec![1, 2, 3];
|
||
|
||
v[99];
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-9-1">Listing 9-1</a>: Attempting to access an element beyond the end of a vector, which will cause a call to <code>panic!</code></figcaption>
|
||
</figure>
|
||
<p>Here, we’re attempting to access the 100th element of our vector (which is at
|
||
index 99 because indexing starts at zero), but the vector has only three
|
||
elements. In this situation, Rust will panic. Using <code>[]</code> is supposed to return
|
||
an element, but if you pass an invalid index, there’s no element that Rust
|
||
could return here that would be correct.</p>
|
||
<p>In C, attempting to read beyond the end of a data structure is undefined
|
||
behavior. You might get whatever is at the location in memory that would
|
||
correspond to that element in the data structure, even though the memory
|
||
doesn’t belong to that structure. This is called a <em>buffer overread</em> and can
|
||
lead to security vulnerabilities if an attacker is able to manipulate the index
|
||
in such a way as to read data they shouldn’t be allowed to that is stored after
|
||
the data structure.</p>
|
||
<p>To protect your program from this sort of vulnerability, if you try to read an
|
||
element at an index that doesn’t exist, Rust will stop execution and refuse to
|
||
continue. Let’s try it and see:</p>
|
||
<pre><code class="language-console">$ cargo run
|
||
Compiling panic v0.1.0 (file:///projects/panic)
|
||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s
|
||
Running `target/debug/panic`
|
||
|
||
thread 'main' panicked at src/main.rs:4:6:
|
||
index out of bounds: the len is 3 but the index is 99
|
||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||
</code></pre>
|
||
<p>This error points at line 4 of our <em>main.rs</em> where we attempt to access index
|
||
99 of the vector in <code>v</code>.</p>
|
||
<p>The <code>note:</code> line tells us that we can set the <code>RUST_BACKTRACE</code> environment
|
||
variable to get a backtrace of exactly what happened to cause the error. A
|
||
<em>backtrace</em> is a list of all the functions that have been called to get to this
|
||
point. Backtraces in Rust work as they do in other languages: The key to
|
||
reading the backtrace is to start from the top and read until you see files you
|
||
wrote. That’s the spot where the problem originated. The lines above that spot
|
||
are code that your code has called; the lines below are code that called your
|
||
code. These before-and-after lines might include core Rust code, standard
|
||
library code, or crates that you’re using. Let’s try to get a backtrace by
|
||
setting the <code>RUST_BACKTRACE</code> environment variable to any value except <code>0</code>.
|
||
Listing 9-2 shows output similar to what you’ll see.</p>
|
||
<!-- manual-regeneration
|
||
cd listings/ch09-error-handling/listing-09-01
|
||
RUST_BACKTRACE=1 cargo run
|
||
copy the backtrace output below
|
||
check the backtrace number mentioned in the text below the listing
|
||
-->
|
||
<figure class="listing" id="listing-9-2">
|
||
<pre><code class="language-console">$ RUST_BACKTRACE=1 cargo run
|
||
thread 'main' panicked at src/main.rs:4:6:
|
||
index out of bounds: the len is 3 but the index is 99
|
||
stack backtrace:
|
||
0: rust_begin_unwind
|
||
at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:692:5
|
||
1: core::panicking::panic_fmt
|
||
at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:75:14
|
||
2: core::panicking::panic_bounds_check
|
||
at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:273:5
|
||
3: <usize as core::slice::index::SliceIndex<[T]>>::index
|
||
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/slice/index.rs:274:10
|
||
4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
|
||
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/slice/index.rs:16:9
|
||
5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
|
||
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:3361:9
|
||
6: panic::main
|
||
at ./src/main.rs:4:6
|
||
7: core::ops::function::FnOnce::call_once
|
||
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
|
||
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
|
||
</code></pre>
|
||
<figcaption><a href="#listing-9-2">Listing 9-2</a>: The backtrace generated by a call to <code>panic!</code> displayed when the environment variable <code>RUST_BACKTRACE</code> is set</figcaption>
|
||
</figure>
|
||
<p>That’s a lot of output! The exact output you see might be different depending
|
||
on your operating system and Rust version. In order to get backtraces with this
|
||
information, debug symbols must be enabled. Debug symbols are enabled by
|
||
default when using <code>cargo build</code> or <code>cargo run</code> without the <code>--release</code> flag,
|
||
as we have here.</p>
|
||
<p>In the output in Listing 9-2, line 6 of the backtrace points to the line in our
|
||
project that’s causing the problem: line 4 of <em>src/main.rs</em>. If we don’t want
|
||
our program to panic, we should start our investigation at the location pointed
|
||
to by the first line mentioning a file we wrote. In Listing 9-1, where we
|
||
deliberately wrote code that would panic, the way to fix the panic is to not
|
||
request an element beyond the range of the vector indexes. When your code
|
||
panics in the future, you’ll need to figure out what action the code is taking
|
||
with what values to cause the panic and what the code should do instead.</p>
|
||
<p>We’ll come back to <code>panic!</code> and when we should and should not use <code>panic!</code> to
|
||
handle error conditions in the <a href="ch09-03-to-panic-or-not-to-panic.html#to-panic-or-not-to-panic">“To <code>panic!</code> or Not to
|
||
<code>panic!</code>”</a><!-- ignore --> section later in this
|
||
chapter. Next, we’ll look at how to recover from an error using <code>Result</code>.</p>
|
||
</body>
|
||
</html>
|