Two kinds of failure: panic! and Result
Rust has no exceptions. It splits failures into two kinds and gives each its own tool:
Unrecoverable: panic!
- A bug: something that should be impossible happened
- Out-of-bounds index, broken invariant, "this can never be None"
- Stops the current thread with a message
- You do not handle it; you fix the code
Recoverable: Result<T, E>
- An expected failure: the file is missing, the input is not a number
- The function returns
Ok(value)orErr(error) - Part of the function's type, so callers cannot ignore it
- The caller decides: retry, default, report, or pass it up
Result is an ordinary enum from the standard library, just like Option from Module 04: enum Result<T, E> { Ok(T), Err(E) }. Because the error is part of the return type, a function signature tells you exactly what can go wrong.
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
return Err(String::from("division by zero"));
}
Ok(a / b)
}
fn main() {
let results = [divide(10, 2), divide(1, 0)];
for r in &results {
match r {
Ok(v) => println!("ok: {v}"),
Err(e) => println!("error: {e}"),
}
}
println!("{:?}", divide(9, 3));
println!("{:?}", divide(9, 0));
}ok: 5
error: division by zero
Ok(3)
Err("division by zero")Runtime panic: panic! stops the program
fn percent(part: u32, whole: u32) -> u32 {
if whole == 0 {
panic!("percent() called with whole = 0");
}
part * 100 / whole
}
fn main() {
println!("{}", percent(1, 4));
println!("{}", percent(1, 0));
}25
thread 'main' (13257569) panicked at main.rs:3:9:
percent() called with whole = 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtraceThe first call prints 25. The second hits panic!: Rust prints the message with the file, line and column, unwinds the stack (running destructors so memory and files are cleaned up), and exits with a non-zero status. Nothing after it runs.
Panicking is right when a zero here means the caller has a bug. If a zero can legitimately arrive (from user input, say), return Result<u32, String> or Option<u32> instead and let the caller decide.
fn percent(part: u32, whole: u32) -> Option<u32> {
if whole == 0 {
return None;
}
Some(part * 100 / whole)
}
fn main() {
println!("{:?}", percent(1, 4));
println!("{:?}", percent(1, 0));
}