C.W.K.
Stream
Lesson 01 of 05 · published

panic! — Unrecoverable Errors

~10 min · errors, panic, unwrap, expect

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

Rust splits failures into two kinds, and the distinction shapes everything: unrecoverable errors that should stop the program, and recoverable errors that callers can handle. This lesson is the first kind: panic!.

When the program should just stop

A panic! means 'something is so wrong that continuing is meaningless.' It unwinds the stack, runs destructors, and aborts the thread. You reach for it on bugs that should never happen — a violated invariant, an index that math guarantees is in-bounds, a 'this case is truly impossible' branch.

unwrap and expect

.unwrap() on an Option or Result panics if it's None/Err. .expect("message") does the same but with your message in the panic — strictly better, because the message tells future-you why you were sure it couldn't fail. Both are panics in disguise: use them when a failure genuinely means a bug, not an expected condition.

panic is for bugs, not for expected failures. A missing file the user might not have created is a recoverable error — return a Result. A failed invariant your own code should have upheld is a bug — panic, and fix the code. Confusing the two gives you either crashes-in-production or error-handling theater.

panic isn't an exception

Don't reach for panic! as a throw-and-catch mechanism. While you can catch an unwinding panic, it's meant for genuinely-unrecoverable situations, not control flow. Recoverable errors flow through Result values, which the rest of this track is about.

Code

expect, index panic, and a deliberate panic!·rust
fn main() {
    let config = vec!["width=80", "height=24"];

    // expect: panic WITH a reason if the invariant breaks
    let first = config.first().expect("config must not be empty");
    println!("{first}");

    // config[99]; // would panic — indexing past the end is a bug

    // A deliberate, documented panic for a truly impossible case:
    let kind = "square";
    let sides = match kind {
        "triangle" => 3,
        "square" => 4,
        other => panic!("unknown shape: {other}"),
    };
    println!("{sides}");
}

External links

Exercise

Write a function that takes a slice and returns its first element using .expect("...") with a meaningful message. Call it once with data and once with an empty slice to see the panic message. Then decide: should this function have returned an Option instead? When is each choice right?
Hint
If an empty slice is an expected, valid input, return Option and let the caller handle None. If an empty slice means a bug upstream, expect documents that assumption and crashes loudly. The domain decides.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.