panic! vs Recoverable Errors
Panics abort unwinding (or abort strategy) for bugs or unrecoverable states. Result handles expected failures callers can respond to.
Recipe
fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
if b == 0 { Err("division by zero") } else { Ok(a / b) }
}
fn index_unchecked(v: &[i32], i: usize) -> i32 {
v[i] // panics if out of bounds - programmer error if misused
}When to reach for this: Result for I/O and validation; panic!/assert! for invariant violations and prototype unwrap.
Working Example
fn parse_config(s: &str) -> Result<u16, String> {
s.trim().parse::<u16>()
.map_err(|e| format!("invalid port: {e}"))
}
fn main() {
match parse_config("8080") {
Ok(p) => println!("{p}"),
Err(e) => eprintln!("config error: {e}"),
}
}What this demonstrates:
- User misconfiguration ->
Result - Caller decides exit/retry
- No panic for expected bad input
Deep Dive
| Use panic | Use Result |
|---|---|
| Logic bug, impossible branch | Network down, bad user input |
assert! in tests | Library public APIs |
unwrap in main prototype | Servers and libraries |
std::panic::catch_unwind rarely in app code - not for normal errors.
Gotchas
unwrapin library - Panics caller's thread. Fix: ReturnResult.- Panic for control flow - Expensive and unclear. Fix:
ResultorOption. - Abort vs unwind -
panic=abortin release changes unwinding. Fix: Know profile settings. - Panic across FFI boundary - Undefined unless caught. Fix: Never panic across FFI.
- Indexing as default -
v[i]panics. Fix:get(i)for user indices.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
Result | Recoverable | Truly impossible if preconditions met |
Option | Absence not error | Need failure reason |
assert! / debug_assert! | Invariants | User input validation |
NonZero types | Prove non-zero at type level | Optional values |
FAQs
panic in tests?
Expected for `should_panic` tests.unwrap vs expect?
expect adds message - slightly better in binaries.panic hook?
Customize logging before unwind.no_std panic?
abort - no unwinding support typically.Iterator::unwrap?
Panics on None - use in trusted pipelines only.Poisoned mutex?
PoisonError - often panic or recover.OOM panic?
Allocator may abort - not catchable as Result.Clippy lints?
warn on `unwrap`/`expect` in libs.HTTP handlers?
Map errors to status codes - no panic per request.Document panics?
rustdoc `# Panics` section for intentional panic APIs.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+.