Error handling: Result, Option, and explicit absence
Translate from error returns and zero-value habits into Result, Option, and ?.
by the end of this lesson you can
- →Uses Option when no match is normal
- →Avoids inventing an error for ordinary absence
- →Keeps caller handling explicit
Overview
Go developers already expect explicit error paths, so Rust's error story is conceptually familiar. The deeper shift is that Rust also models absence explicitly with Option, and the type system prevents callers from mixing absence and failure casually.
In Go, you often
return (value, err) and sometimes rely on zero values or nil to mean absence.
In Rust, the common pattern is
to use Result<T, E> for failure, Option<T> for absence, and ? to propagate errors without hiding them.
why this difference matters
Rust sharpens a habit Go developers already have: failure and absence become different contracts, not merely different values.
Go
user, err := lookupUser(id)
if err != nil {
return err
}
if user == nil {
return ErrMissing
}Rust
let user = lookup_user(id).ok_or(UserError::Missing)?;Deeper comparison
Go version
cfg, err := loadConfig(path)
if err != nil {
return nil, err
}
return cfg, nilRust version
fn load(path: &str) -> Result<Config, ConfigError> {
let cfg = load_config(path)?;
Ok(cfg)
}Reflect
Why is it useful to separate 'not found' from 'operation failed' more aggressively than many Go APIs do?
what a strong answer notices
A strong answer mentions clearer contracts, less guesswork at the call site, and better modeling of ordinary absence.
Rewrite
Rewrite this Go function into Rust and choose Option or Result intentionally.
Rewrite this Go
func FirstAdmin(users []User) *User {
for _, user := range users {
if user.Admin {
return &user
}
}
return nil
}what good looks like
- Uses Option when no match is normal
- Avoids inventing an error for ordinary absence
- Keeps caller handling explicit
Practice
Design a Rust config loader that distinguishes parse failure from an optional field that is simply missing.
success criteria
- Uses Result for actual failure
- Uses Option where absence is expected
- Explains how the caller handles both cases differently
Common mistakes
- Using Result when Option is the clearer contract.
- Treating None like a Rust version of any nil-ish value.
- Flattening distinct error cases into strings too early.
takeaways
- ●Rust sharpens a habit Go developers already have: failure and absence become different contracts, not merely different values.
- ●A strong answer mentions clearer contracts, less guesswork at the call site, and better modeling of ordinary absence.
- ●Uses Result for actual failure