Intermediate · Error handling and collections · topic 22 of 50 · 20 min
panic! and unrecoverable errors
Explanation
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
fn main() {
let marks = vec![70, 80, 90];
println!("{}", marks[10]);
}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 backtraceBy 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.
RUST_BACKTRACE=1 cargo runPanicking on purpose
fn percent(part: u32, total: u32) -> u32 {
if total == 0 {
panic!("percent called with total = 0");
}
part * 100 / total
}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.
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");When is panicking acceptable?
| Situation | Panic OK? | Why |
|---|---|---|
| A quick experiment or example | Yes | You want to see the happy path first |
| Tests | Yes | A panic fails the test, which is what you want |
| A truly impossible case you can prove | Yes, with expect explaining why | For example parsing a hard-coded valid number |
| Bad user input, missing file, network down | No | These are expected; return a Result and show a message |
| A library function | Rarely | Let the caller decide how to handle problems |
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.
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
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.
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)]);
}Practice (0/3 done)
Which situation should NOT use panic or unwrap in a real app?
What is the advantage of expect("msg") over unwrap()?
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
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Result and the ? operatorError handling and collections · same module
- Custom error typesError handling and collections · same module
- Vec<T>: growable listsError handling and collections · same module
- panic and recover: only for the truly unexpectedGo Programming (backend and cloud) · Pointers, interfaces and errors
- Interview questions: traits, errors, pointers and concurrencyRust careers and interviews
- ThreadsConcurrency and async
Questions people ask about this
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.
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
What does the ? operator do on an Err?Related
Answer: Returns the error from the current function
? propagates the error to the caller.
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
- 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.
- Module 6 exam: Error handling and collections · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Modules, crates, traits and generics. 50 topics left in the course before the final exam.