Option, Result, and errors: explicit absence and explicit failure
Translate Python's None and exception habits into Rust's explicit sum types and propagation patterns.
by the end of this lesson you can
- →Chooses Option when absence is normal
- →Avoids inventing an error when there is only no match
- →Keeps caller handling explicit
Overview
Python often uses `None` for absence and exceptions for failure. Rust splits those concepts into `Option` and `Result`, which makes the shape of success, absence, and failure visible in the type itself.
In Python, you often
use `None` as a flexible placeholder and exceptions to signal failure, sometimes without a hard distinction between the two.
In Rust, the common pattern is
to use `Option<T>` when a value may be absent and `Result<T, E>` when an operation can fail.
why this difference matters
Separating absence from failure makes APIs easier to reason about and prevents callers from guessing what a missing value really means.
Python
user = lookup_user(id)
if user is None:
raise ValueError("missing user")Rust
let user = lookup_user(id).ok_or("missing user")?;Deeper comparison
Python version
def load_profile(user_id):
profile = lookup_profile(user_id)
if profile is None:
raise LookupError("missing profile")
return profileRust version
fn load_profile(user_id: UserId) -> Result<Profile, ProfileError> {
let profile = lookup_profile(user_id).ok_or(ProfileError::Missing)?;
Ok(profile)
}Reflect
Why is it useful to force the caller to distinguish 'no value' from 'operation failed'?
what a strong answer notices
A strong answer mentions clearer API contracts, fewer ambiguous call sites, and more deliberate error handling.
Rewrite
Rewrite this Python function so the Rust version uses Option or Result intentionally rather than a generic null-or-error mix.
Rewrite this Python
def first_admin(users):
for user in users:
if user.is_admin:
return user
return Nonewhat good looks like
- Chooses Option when absence is normal
- Avoids inventing an error when there is only no match
- Keeps caller handling explicit
Practice
Design a Rust function that reads configuration from disk, validates it, and returns a typed result that distinguishes parse failure from a missing field.
success criteria
- Uses Result for actual failure
- Explains where Option still appears inside the parsing flow
- Shows how the caller would handle both cases clearly
Common mistakes
- Using Result everywhere, even when Option is the clearer contract.
- Treating Option like an inconvenience rather than a precise signal of absence.
- Flattening all failure modes into strings too early.
takeaways
- ●Separating absence from failure makes APIs easier to reason about and prevents callers from guessing what a missing value really means.
- ●A strong answer mentions clearer API contracts, fewer ambiguous call sites, and more deliberate error handling.
- ●Uses Result for actual failure