Rust: Avoid unwrap()
Rule
Do not use .unwrap() on Option or Result types unless absolutely certain the value is always present.
When writing or editing Rust code in this project:
- Never use
unwrap()on values that can reasonably fail — file I/O, network calls, user input, parsing, indexing, HashMap lookups, etc. unwrap()is only acceptable when the code can mathematically prove the value is always present, e.g.:Some(x)that was just constructed in the same expression- A
Resultfrom an operation that by construction cannot fail (e.g.,1_usize.checked_add(0).unwrap()) - Test code where panicking on unexpected states is intentional
Alternatives
Replace unwrap() with one of the following, depending on context:
| Situation | Replacement | Example |
|---|---|---|
| Need the value, want to propagate errors | ? operator |
let val = result?; |
| Need the value, have a sensible default | .unwrap_or() / .unwrap_or_default() |
map.get(k).unwrap_or(0) |
| Need the value, want to provide context on failure | .expect("reason") |
file.expect("config must be readable") |
| Need the value, custom error type | .map_err() then ? |
`result.map_err( |
| Want to handle both branches | match |
match opt { Some(v) => ..., None => ... } |
| Want to handle both branches with fallback | .or_else() / .or() |
`opt.or_else( |
| Option on a collection lookup | .get() + fallback |
map.get(key).copied().unwrap_or_default() |
Decision Flow
Can the value ever be None/Err in practice?
→ YES: Use ? or match or unwrap_or/expect with clear reason
→ UNCERTAIN: Assume YES — use ? or match or unwrap_or
→ NO (provably always Ok/Some): unwrap() is acceptable
Review Checklist
When reviewing or editing Rust files, check:
- No
.unwrap()calls on I/O results, parse results, collection lookups, or any fallible operation - Every
.expect()has a meaningful, specific reason string (not just "should work") - Error types implement proper conversion so
?chains cleanly - Test code that intentionally uses
unwrap()is clearly in a#[test]or test helper context
Examples
Bad — panics on missing key:
let config = settings.get("host").unwrap();
Good — provides fallback:
let config = settings.get("host").unwrap_or("localhost");
Bad — panics on file read:
let data = fs::read_to_string(path).unwrap();
Good — propagates error:
let data = fs::read_to_string(path)?;
Bad — vague expect:
let port = str::parse::<u16>(s).expect("parse failed");
Good — specific expect:
let port = str::parse::<u16>(s).expect("port number must be valid u16");
Acceptable — provably safe:
// Just created this Option, it is always Some
Some(42).unwrap()