Panics, unwrap, expect, and assertions
Rule
Return Result for recoverable failures; panic only for violated invariants or impossible states, and prefer invariant-focused expect over bare unwrap.
Why
Panics provide no structured recovery path, so caller input, I/O, parsing, configuration, and external failures remain ordinary errors.
Do
- Return
Resultfor caller-controlled or environmental failures. - Use
expectonly when failure proves a hard-coded value, fixture, or invariant is wrong. - State the invariant in each
expectmessage. - Use assertions for tests and internal invariants.
- Use
debug_assert!only for checks unnecessary to release correctness. - Use
unreachable!only after construction has ruled out the state. - Document public panic behavior with
# Panics.
Avoid
- Do not use
unwrap,expect, or panic for recoverable runtime failure. - Do not use bare
unwrapoutside tests; workspace policy denies it. - Do not write messages such as
should workorfailed. - Do not use
unreachable!for states reachable from external input. - Do not rely on
debug_assert!for safety, security, or validation. - Do not commit
todo!()orunimplemented!()on production paths.
Library vs Application
Libraries return caller-controlled failures. Applications may fail fast for violated build-time invariants, but operator mistakes still receive clean diagnostics.
Example
#![allow(unused)]
fn main() {
pub fn parse_port(raw: &str) -> Result<u16, std::num::ParseIntError> {
raw.parse()
}
const DEFAULT_PORT: &str = "8080";
pub fn default_port() -> u16 {
DEFAULT_PORT
.parse()
.expect("DEFAULT_PORT should be a valid u16")
}
}
Exceptions
- Use
unwrapin a short test when the failure location is already obvious. - Use panic in disposable examples or prototypes.
- Panic on an impossible internal state when an error would falsely imply recovery.