17. panic! and Unrecoverable Errors
panic is Rust's way of saying "this is unrecoverable, stop now." Array bounds, integer division by zero, and `.unwrap()` on None all trigger panic automatically. This lesson covers how panic works, unwind vs. abort, and when to invoke panic on purpose.
What you'll learn
- 1List situations that automatically panic
- 2Trigger an explicit panic with the `panic!` macro
- 3Distinguish unwind vs. abort
- 4Use assert! / assert_eq! / debug_assert!
- 5Choose between Result-handling and panic
Overview
Panic resembles an unchecked exception in other languages, but Rust's messages are clean and stack traces are tidy. Most panics are **safety nets that surface bugs immediately** β debug-build integer overflow, .unwrap() on None, etc.
Core Concepts
1) Situations that auto-panic
- Array / slice index out of bounds
- Integer overflow (debug builds)
- Integer division by zero
- Option::unwrap() on None, Result::unwrap() on Err
- RefCell borrow-rule violations at runtime
2) Explicit panic
panic!("intentional stop β TODO: implement");
assert!(n > 0, "n must be positive");
assert_eq!(actual, expected, "values differ");3) unwind vs. abort
| Strategy | Behavior | Default |
|---|---|---|
| unwind | Runs Drops, releases resources | Dev/release default |
| abort | Exit immediately, no Drop | Opt-in via Cargo.toml panic = "abort" (smaller binaries) |
4) When to panic vs. return Result
- **panic** β unrecoverable bugs / invariant violations (continuing would be worse)
- **Result** β user input / external I/O / things that legitimately fail
Hands-on Examples
fn divide(a: i32, b: i32) -> i32 {
assert!(b != 0, "cannot divide by zero");
a / b
}
fn main() {
println!("{}", divide(10, 2)); // 5
println!("{}", divide(10, 0)); // thread 'main' panicked at 'cannot divide by zero'
}Debug-only assertion (stripped in release):
fn process(v: &[i32]) {
debug_assert!(!v.is_empty(), "empty slice passed");
// ... real processing
}Switch panic strategy via Cargo.toml:
[profile.release]
panic = "abort" # smaller binaries, no DropSee stack traces:
RUST_BACKTRACE=1 cargo run
# Detailed call stack up to the panic siteCommon Mistakes
Q. Is .unwrap() also a panic?
A. Yes β None/Err triggers panic. For user input or I/O prefer `?` or `.unwrap_or()`.
Q. Can I catch panics?
A. `std::panic::catch_unwind` exists but is not a general error-handling tool β it's for FFI boundaries or thread isolation. Day to day, use Result.
Q. debug_assert! gets stripped in release β can I rely on it?
A. It's a dev-time check. **For checks that must remain in release, use `assert!`** (always active).
Recap
- panic = unrecoverable, immediate stop
- Bounds / overflow / .unwrap on None trigger it automatically
- Explicit: panic! / assert! / assert_eq! macros
- panic for bugs / invariant violations, Result for legitimate failures
- RUST_BACKTRACE=1 for stack traces
Try It Yourself
- Intentionally trigger an out-of-bounds index and read the panic message + stack trace
- Write a simple test function with assert_eq! and verify it panics on a wrong value
- Switch release profile to abort and compare binary size
All lecture materials and example code are openly available on GitHub.
View on GitHub β