In this lesson, you will learn how to distinguish between "expected" errors and "unrecoverable" states to define safe boundaries for your program’s stability.
Rust’s philosophy is that you should use Result for anything that can possibly go wrong during normal operation. However, there are rare scenarios where a program reaches an impossible state—a logical contradiction that makes further execution dangerous or meaningless. This is where a panic boundary comes into play.
Where Panics are Permissible
A panic is permissible when your program is in a "corrupted" state where you cannot safely continue. This is common in:
- Logic assertions: Using
assert!to check for invariants that should be true (like index math that shouldn't overflow). - Unreachable code: Using
unreachable!()in amatchstatement where every variant has already been handled, but the compiler cannot prove it. - Critical initialization: If your application cannot start without a required system resource (like an essential configuration file), panicking during startup is often preferred over running in a broken state.
Where Panics must be Avoided
You must strictly avoid panicking in libraries or any code that processes user input. If a user provides an invalid network packet or a bad file path, that is an error, not a logical contradiction.
// AVOID: Panicking on user input
fn get_user_age(input: &str) -> u32 {
input.parse().expect("Expected a number!") // Bad: crashes the whole thread
}
// PREFER: Returning a Result
fn get_user_age(input: &str) -> Result<u32, MyError> {
input.parse().map_err(|_| MyError::InvalidInput)
}
The Boundary of Recovery
While you can recover from panics using std::panic::catch_unwind, this is not a substitute for Result. catch_unwind is intended for systems like web servers or plugin runners where you need to isolate a panic to prevent it from crashing the entire process (e.g., stopping one user's request without killing the whole server).
If you find yourself writing code that constantly needs to catch panics, it is a sign that your API boundaries are likely designed incorrectly and should be returning Result instead.
Key Takeaway: Use Result for all foreseeable errors; reserve panics only for logical impossibilities that represent a total breakdown of your program's integrity.