Files
docs-rust/ch09/ch09-01-unrecoverable-errors-with-panic.html
2026-06-22 21:27:36 +05:30

164 lines
10 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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 theres 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>Lets 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, youll 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 its 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 elses 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, lets look at another example and see what its 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, were 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, theres 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
doesnt 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 shouldnt 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 doesnt exist, Rust will stop execution and refuse to
continue. Lets 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. Thats 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 youre using. Lets 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 youll 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: &lt;usize as core::slice::index::SliceIndex&lt;[T]&gt;&gt;::index
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/slice/index.rs:274:10
4: core::slice::index::&lt;impl core::ops::index::Index&lt;I&gt; for [T]&gt;::index
at file:///home/.rustup/toolchains/1.85/lib/rustlib/src/rust/library/core/src/slice/index.rs:16:9
5: &lt;alloc::vec::Vec&lt;T,A&gt; as core::ops::index::Index&lt;I&gt;&gt;::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>Thats 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 thats causing the problem: line 4 of <em>src/main.rs</em>. If we dont 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, youll 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>Well 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, well look at how to recover from an error using <code>Result</code>.</p>
</body>
</html>