Rust Panic
A panic is Rust’s way of stopping a program immediately when something has gone unrecoverably wrong. You’ve actually already caused a few without meaning to, if you’ve followed along with earlier pages — indexing past the end of an array, or calling .unwrap() on a None, both panic.
Triggering a panic manually
Section titled “Triggering a panic manually”fn main() { panic!("something went badly wrong");}thread 'main' panicked at src/main.rs:2:5:something went badly wrongnote: run with `RUST_BACKTRACE=1` environment variable to display a backtraceWhen this runs, the program prints that message and stops — nothing after the panic! executes.
Things that panic without you calling panic! directly
Section titled “Things that panic without you calling panic! directly”A few operations you’ve already met will panic on their own if something’s wrong. Each example below reads its risky value through a function parameter rather than a hardcoded literal — with a literal index or a literal 0 right there in the code, the compiler can often work out at compile time that the operation always fails, and refuses to build at all instead of letting it run and panic.
fn get(numbers: &[i32], index: usize) -> i32 { numbers[index] // panics: index out of bounds}
fn main() { let numbers = [1, 2, 3]; println!("{}", get(&numbers, 10));}fn main() { let value: Option<i32> = None; println!("{}", value.unwrap()); // panics: called `Option::unwrap()` on a `None` value}fn divide(a: i32, b: i32) -> i32 { a / b // panics: attempt to divide by zero}
fn main() { let a = 10; let b = 0; println!("{}", divide(a, b));}Each of these is Rust refusing to let your program continue in a state it can’t make sense of, rather than quietly returning garbage.
panic! vs Result
Section titled “panic! vs Result”This is the real decision you’ll make often: should this failure crash the program, or should the caller get a chance to handle it?
panic!— for bugs and situations that should never happen if the code is correct. There’s no sensible way to recover, so stopping immediately (before doing more damage) is the safest option.Result(covered on the previous page) — for failures a caller might reasonably expect and want to handle, like bad user input, a missing file, or a failed network request.
// Bad input from a user — recoverable, use Resultfn parse_age(input: &str) -> Result<u32, String> { input.parse().map_err(|_| String::from("not a valid age"))}
// An invariant that should be impossible if the code is correct — panicfn get_first(numbers: &[i32]) -> i32 { if numbers.is_empty() { panic!("get_first called with an empty slice"); } numbers[0]}unwrap() and expect() are shortcuts to panicking
Section titled “unwrap() and expect() are shortcuts to panicking”You’ve seen .unwrap() on both Option and Result — it’s really just a shortcut for “give me the value, or panic if there isn’t one.” .expect("message") does the same thing but lets you attach a more useful message:
fn main() { let input = "42"; let number: i32 = input.parse().expect("input should be a valid number"); println!("{number}");}If parsing fails, the panic message includes your text, which makes debugging a lot easier than the generic message .unwrap() gives you.
panic!stops the program immediately with an error message.- Some standard operations (bad array index,
.unwrap()onNone, division by zero) panic automatically. - Use
panic!for bugs/invariants that should be impossible; useResultfor failures a caller can reasonably handle. .expect("message")is like.unwrap(), but with a message that makes the panic easier to debug.
Quick check
Section titled “Quick check”Quick check
1. What does calling panic! do?
2. According to the rule of thumb on this page, when should you use panic! instead of Result?
3. What's the difference between .unwrap() and .expect('message')?
Score: 0 / 3