RustArena

Step 3 of 7

unwrap, expect and safe defaults

Writing a match for every Result gets long. Result (and Option) have shortcut methods for the common cases.

unwrap and expect

fn main() {
    let port: u16 = "8080".parse().expect("the default port is a valid number");
    println!("port {port}");
    let qty: u32 = "ten".parse().unwrap();
    println!("quantity {qty}");
}
port 8080

The second parse fails, so the program panics with: called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }. With expect, the message would start with your text instead.

When is unwrap or expect fine?

Both turn a recoverable error into a crash. That's acceptable when:

They're not fine for:

Workplace note: many teams don't allow a bare unwrap() in production code and ask for expect with a message that states the expectation ("config was validated at startup") instead. Clippy can enforce this with its unwrap_used lint, which is off by default.

Safe defaults: unwrap_or and unwrap_or_default

Often there's a sensible fallback. unwrap_or(value) gives the Ok value, or value on error. unwrap_or_default() falls back to the type's default value: 0 for numbers, false for bool, an empty String or Vec.

fn main() {
    let retries: u32 = "abc".parse().unwrap_or(3);
    let workers: u32 = "8".parse().unwrap_or(1);
    let timeout: u32 = "".parse().unwrap_or_default();
    let debug: bool = "maybe".parse().unwrap_or_default();
    println!("retries={retries} workers={workers} timeout={timeout} debug={debug}");

    let nickname: Option<&str> = None;
    println!("hello, {}", nickname.unwrap_or("guest"));
}
retries=3 workers=8 timeout=0 debug=false
hello, guest

The same methods work on Option, as the last line shows: None falls back to "guest".

Common mistakes

fn main() {
    let qty: u32 = "x".parse().unwrap_or("0");
    println!("{qty}");
}
error[E0308]: mismatched types
  |     let qty: u32 = "x".parse().unwrap_or("0");
  |                                --------- ^^^ expected `u32`, found `&str`

Your task

Write fn parse_settings(retries: &str, verbose: &str) -> (u32, bool) for a tool's settings. Trim both inputs.

Challenge 2

Fill in the blanks so each volume setting falls back to 50 when it isn't a valid u8, and each boost falls back to the default 0. The program prints:

"75": volume 75, boost 75
"loud": volume 50, boost 0
"": volume 50, boost 0
"300": volume 50, boost 0

Challenge 3

This temperature converter crashes when the user types something that isn't a number. Keep the expect on read_line (if reading stdin fails, something is badly wrong and crashing is fine), but replace the unwrap() on the parse: when the trimmed input isn't a valid f64, print not a temperature: <trimmed input> instead of crashing.

Previous: Handling a Result with match Next: The ? operator