Skip to content

Intermediate · Error handling and collections · topic 22 of 50 · 20 min

panic! and unrecoverable errors

Explanation

A panic stops the current thread with an error message; it is for bugs and situations that should never happen. unwrap() and expect() panic when a value is None or Err. They are fine in quick experiments, tests and truly impossible cases, but real programs should usually return errors instead of crashing.

Two kinds of errors

Things go wrong in every program. Rust splits errors into two kinds. Recoverable errors are expected problems the program can handle: a file is missing, the user typed letters instead of a number, the network is down. Unrecoverable errors are bugs: an index past the end of a list, a value that "can never be zero" being zero. For recoverable errors Rust uses the Result type (next topic). For bugs it uses panic.

What a panic does

rust
fn main() {
    let marks = vec![70, 80, 90];
    println!("{}", marks[10]);
}
Indexing past the end is a bug, so Rust panics.
output
thread 'main' panicked at src/main.rs:3:20:
index out of bounds: the len is 3 but the index is 10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The message says what went wrong and the file, line and column.

By default, a panic unwinds: Rust walks back up the call stack, dropping every value so memory and files are cleaned up, and then the thread stops. If the main thread panics, the program exits with an error code. A backtrace is the list of function calls that led to the panic; set RUST_BACKTRACE=1 to see it when you debug.

bash
RUST_BACKTRACE=1 cargo run
On Windows PowerShell: $env:RUST_BACKTRACE=1; cargo run

Panicking on purpose

rust
fn percent(part: u32, total: u32) -> u32 {
    if total == 0 {
        panic!("percent called with total = 0");
    }
    part * 100 / total
}
panic! takes a message like println!. Use it when continuing would be wrong.

unwrap and expect

Option and Result have two shortcut methods. unwrap() gives the value inside, or panics if there is none. expect("message") does the same but with your own message, which makes the panic much easier to understand. Prefer expect over unwrap when you use either.

rust
let n: u32 = "42".parse().unwrap();            // fine: "42" is valid
let port: u16 = std::env::var("PORT")
    .expect("PORT must be set")
    .parse()
    .expect("PORT must be a number");
A good expect message says what you expected to be true.

When is panicking acceptable?

SituationPanic OK?Why
A quick experiment or exampleYesYou want to see the happy path first
TestsYesA panic fails the test, which is what you want
A truly impossible case you can proveYes, with expect explaining whyFor example parsing a hard-coded valid number
Bad user input, missing file, network downNoThese are expected; return a Result and show a message
A library functionRarelyLet the caller decide how to handle problems
Think of it like this: A panic is like pulling the emergency chain on a train. It is the right thing when something is truly dangerous, like a fire. But you do not pull it because a passenger has the wrong seat; the ticket checker handles that calmly. Result is the ticket checker.

Common mistakes

  • unwrap() on user input or file reads in a real program. One typo crashes everything. Use match or ? (next topic).
  • unwrap() everywhere with no message. When it panics, you do not know which one failed. Use expect("...") with a clear reason.
  • Thinking a panic is "exception handling" like try/catch in Java. Rust does not use panics for normal error flow.
  • Ignoring the line number in the panic message. It points to the exact place; start debugging there.
Tip: In a release build you can set panic = "abort" in Cargo.toml to skip unwinding and make the binary smaller. Most apps keep the default.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

panic! and unrecoverable errors · 1/6

Expected problem → Result Bug → panic

Rust has two kinds of errors. Recoverable ones, like a missing file, and unrecoverable ones, which are bugs.

Prefer a video by a teacher? Find free videos on “rust panic unwrap expect when to use” on YouTube ↗

In real life: A config value that must exist

A payment microservice cannot work without its database URL. At start-up it reads the setting with expect("DATABASE_URL must be set"). If someone forgets it while deploying, the service stops at once with a clear message, instead of starting and failing on the first customer payment. For each customer request, however, the service never panics: bad requests get an error response.

rust
use std::env;

fn main() {
    let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    println!("Connecting to {}...", &db_url[..db_url.len().min(12)]);
}
Without the variable, it panics with: DATABASE_URL must be set: NotPresent

Practice (0/3 done)

Task 1 · Question

Which situation should NOT use panic or unwrap in a real app?

Task 2 · Question

What is the advantage of expect("msg") over unwrap()?

Task 3 · Practice

Write a program that panics in three ways: an index out of bounds, unwrap on None, and your own panic! with a message. Run each (comment the others out) and copy the first line of each message. Then run one with RUST_BACKTRACE=1 and find your own function in the backtrace.

Quick quiz

Question 1

1.Which kind of error is a missing file?

Question 2

2.What does unwrap() do on an Err value?

Question 3

3.What happens to values in scope during a default panic?

Question 4

4.How do you see the chain of calls that led to a panic?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which kind of error is a missing file?

Answer: Recoverable: return a Result

A missing file is expected in normal use, so handle it.

What does unwrap() do on an Err value?

Answer: Panics

unwrap panics if there is no Ok value.

What happens to values in scope during a default panic?

Answer: They are dropped as the stack unwinds

Unwinding runs drop for each value on the way up.

How do you see the chain of calls that led to a panic?

Answer: Set RUST_BACKTRACE=1

The backtrace lists the function calls.

Why does regexp.MustCompile panic instead of returning an error?Related

Answer: It is meant for fixed patterns in your code, where a bad pattern is a programming mistake

Must helpers are for constant inputs; failures indicate bugs.

From panic and recover: only for the truly unexpected

Which is a good answer to "When do you panic?"Related

Answer: For bugs and impossible states; expected failures return Result

Panics are for programmer errors, not normal failures.

From Interview questions: traits, errors, pointers and concurrency

What does Some(5).unwrap_or(0) give? And None.unwrap_or(0)?Related

Answer: 5 and 0

unwrap_or returns the inner value, or the default for None.

From Enums and Option

What does the ? operator do on an Err?Related

Answer: Returns the error from the current function

? propagates the error to the caller.

From Result and the ? operator

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Error handling and collections” before its exam: Result and the ? operator, Custom error types, Vec<T>: growable lists, HashMap and HashSet.
  2. Module 6 exam: Error handling and collections · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Modules, crates, traits and generics. 50 topics left in the course before the final exam.

← Previous: Pattern matching: match, if let and let else