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
.unwrap()gives you the value insideOk, or panics if it's anErr..expect("message")does the same, but panics with your message in front of the error.
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:
- In tests. A panic fails the test, which is exactly what you want.
- In prototypes and quick scripts. Crashing on bad input is fine while you explore. Search for
unwrapwhen you turn the prototype into real code. - When failure is impossible (an invariant).
"8080".parse::<u16>()on a literal you wrote can't fail. Useexpectand say why it can't fail. If it ever does panic, the message tells the next developer which assumption broke.
They're not fine for:
- Anything from outside your program: user input, files, network data, environment variables. It will be wrong eventually, and a typo shouldn't crash your program.
- Library code. A library can't know whether its caller can survive a crash. Return a
Resultand let the caller decide.
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
- A fallback of the wrong type. The fallback must have the same type as the success value:
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`
- Hiding real problems with a default. If a missing setting is a mistake the user must fix, falling back silently makes the bug hard to find. Use a default only when it's a genuinely reasonable choice.
expectmessages that only repeat the failure..expect("parse failed")adds nothing; the error already says that. Say what you expected:.expect("PORT should be a number").
Your task
Write fn parse_settings(retries: &str, verbose: &str) -> (u32, bool) for a tool's settings. Trim both inputs.
retriesis parsed as au32; if that fails, use3(withunwrap_or)verboseis parsed as abool(onlytrueandfalseare valid); if that fails, use the defaultfalse(withunwrap_or_default)
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.