Panic & Unwrap Scenarios
Panics indicate bugs or unrecoverable states. Replace unwrap on fallible paths with ? and typed errors; reserve panics for invariant violations and tests.
Recipe
// Before
let port: u16 = env::var("PORT").unwrap().parse().unwrap();
// After
let port: u16 = env::var("PORT").context("PORT")?.parse().context("PORT parse")?;Working Example
| Site | Replace with |
|---|---|
Option::unwrap | ok_or, context, if let |
Result::unwrap | ? |
index out of bounds | get returning Option |
assert! in lib | Result or debug_assert! |
pub fn get_user(id: UserId) -> Result<User, Error> {
repo.find(id)?.ok_or(Error::NotFound(id))
}Deep Dive
catch_unwind at FFI boundary only. panic=abort in prod changes unwinding behavior. Backtrace: RUST_BACKTRACE=1.
Gotchas
- unwrap in Default::default - panics on missing config. Fix: no Default; use
try_new. - expect without message - opaque prod logs. Fix: meaningful expect or Result.
- panic in Drop - abort during unwind. Fix: infallible Drop.
- array[i] without check - use
.get(i). - todo! in prod path - remove before ship.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
? operator | recoverable errors | truly impossible if documented |
unwrap_or_else | fallback value | error context needed |
assert! | test/invariant | user input validation |
FAQs
panic in tests?
#[should_panic] or catch_unwind for panic tests.
main panic?
Print anyhow chain; set non-zero exit.
axum panic?
Default catches and 500; use Result for 4xx.
custom hook?
std::panic::set_hook for logging; careful in embedded.
abort vs unwind?
abort smaller binary; no catch_unwind recovery.
find unwraps?
rg '\.unwrap\(' and clippy unwrap_used.
library policy?
deny unwrap in public paths via clippy.
option expect?
ok_or maps to domain error variant.
join panic?
Task panic propagates on await unless catch in task.
postmortem?
Add test reproducing former panic path.
Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.