~/ ~/documents ~/software ~/pictures github (opens in new tab)

Don’t Panic

Errors in production systems belong to two distinct domains: expected state failures and broken system invariants. Result handles state. panic! handles broken guarantees.

The Rust Book establishes this separation in Chapter 9 (“Error Handling”) by splitting failures into recoverable (Result<T, E>, Option<T>) and unrecoverable (panic!) paths.

Chapter 9 identifies specific contexts where panicking may be highly appropriate:

While these shortcuts help with quick learning and prototyping, they must not leak into production. Methods like .unwrap() and .expect() turn expected runtime failures into system panics. They blur the boundary between operational error handling and process crashes. This risk is not merely theoretical; the Cloudflare outage on 18 November 2025 was caused by an unhandled .unwrap() on a None value, triggering a cascade of failures across their global network.

Poor Implementation:
// Bad: Converts runtime IO failure into process termination
let file = File::open("config.json").unwrap();

Explicit error propagation surfaces failure context to caller sites where recovery belongs.

Improved Implementation:
// Good: Preserves caller control and error context
let file = File::open("config.json").unwrap_or_default(|e| {
    eprintln!("Failed to open config.json: {}", e);

    // Decide whether to recover, log, or propagate the error
});

The responsibility for handling the error is now explicit, the call site decides whether to recover, log, or propagate the error further.

In safety-critical software, an unhandled panic is not a minor bug; it is a violation of fundamental safety properties. Safety-critical engineering requires total determinism, explicit error domains, and zero unhandled failure states at compile time. Allowing arbitrary panics breaks process guarantees and risks catastrophic state corruption.

Safety enforcement belongs in the build pipeline. Enforce compile-time panic elimination across the codebase using explicit Clippy lints:

#![deny(clippy::unwrap_used)]
#![deny(clippy::expect_used)]
#![deny(clippy::panic)]

Static analysis converts runtime panic risks into immediate compilation errors.

Panics are not an alternative control flow. Use panic! or assert! exclusively when continuing execution would compromise memory safety or corrupt internal state. A panic at an execution boundary must never leak across foreign interfaces or crash orchestration services without isolation. Catch panics at process boundaries, convert them into explicitly structured error types, and isolate the failure.