🎓 A1Learner
🔍

Rust Panic Strategy

A panic strategy is a program's plan for the panics that slip through.

Replace the report with set_hook

set_hook swaps the default panic report for your own — the panic itself still happens. The hook travels in a box, a tool from the error lessons later in this course. The shape of the idea is simpler. Catch the panic, then print your own line:

let job = panic::catch_unwind(|| panic!("database gone"));
if job.is_err() {
    println!("a bug was logged");
}

A thread can panic alone

Spawn work in a thread, then join it. The join Result tells you how the thread ended.

let worker = std::thread::spawn(|| {
    panic!("database gone");
});
let outcome = worker.join();
println!("worker panicked: {}", outcome.is_err());

panic_any throws any value

The payload need not be a message. Here it is an error code, read back by the catcher.

let caught = panic::catch_unwind(|| panic::panic_any(42_i32));
if let Err(payload) = caught {
    if let Some(code) = payload.downcast_ref::<i32>() {
        println!("error code: {}", code);
    }
}

Abort ends the whole process

One Cargo.toml setting chooses abort over unwinding, when a smaller binary matters more.

[profile.release]
panic = "abort"

Catch at the border of a job

The whole strategy in one shape: each unit of work runs inside catch_unwind, and the worker moves on.

let job = panic::catch_unwind(|| do_one_job());
match job {
    Ok(()) => println!("job finished"),
    Err(_) => println!("job failed; the worker is still alive"),
}

Example

use std::panic;

fn risky_step() {
    let values = [1, 2];
    let i: usize = "5".parse().unwrap();
    println!("{}", values[i]);
}

fn main() {
    let job = panic::catch_unwind(|| {
        risky_step();
    });
    match job {
        Ok(()) => println!("job finished"),
        Err(_) => println!("job failed; the worker is still alive, moving to the next job"),
    }
    println!("worker done");
}

Key points

  • A panic strategy decides in advance what ends, what survives, and what the user sees.
  • Libraries panic only on broken contracts (a caller-side bug).
  • Libraries return Result for every expected failure, so the caller keeps the choice.
  • That is The Rust Book's rule.
  • Applications decide the final answer.
  • Report the bug.
  • Save what can be saved.
  • Then exit — or restart just the failed piece.
  • With abort, catch_unwind cannot save you — nothing unwinds, so there is nothing to catch

In this example, risky_step indexes position 5 in a two-item list — a bug, so it panics. The panic unwinds only as far as the catch_unwind around the job. main reads the Err, prints that the job failed, and carries on to worker done. The panic also prints its usual report to stderr. Catching stops the unwinding, not the report. A set_hook could replace that report with a log line. This is the worker strategy: small borders, caught panics, and a program that survives its own bugs.

Note

Unwinding runs destructors on the way up, so values are cleaned up even during a crash.

Abort skips that walk entirely.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

A server must keep answering other requests when one request hits a bug. Which setup fits?