Result, Option, and error handling: from exceptions and undefined to explicit outcomes
Translate JavaScript's thrown errors and nullable values into Rust's explicit outcome types.
by the end of this lesson you can
- →Uses `Option` if absence is normal
- →Uses `Result` if failure is part of the contract
- →Makes the return shape more explicit than the JavaScript original
Overview
JavaScript uses exceptions, rejected Promises, `null`, and `undefined` to describe missing values and failure. Rust separates those concerns more clearly with `Result` for fallible operations and `Option` for absence.
In JavaScript/Node, you often
let failures throw and use `undefined` or `null` for missing data, sometimes mixing the two ideas in the same API.
In Rust, the common pattern is
to model absence with `Option` and failure with `Result`, keeping both visible in function signatures.
why this difference matters
This makes reading APIs easier. You can see whether a function can fail, whether a value can be missing, and what the caller must handle.
JavaScript/Node
const user = users.find((user) => user.id === id);Rust
let user = users.iter().find(|user| user.id == id);Deeper comparison
JavaScript/Node version
async function loadUser(id) {
const user = await repo.get(id);
if (!user) {
throw new Error("missing user");
}
return user;
}Rust version
fn load_user(id: u64, repo: &Repo) -> Result<User, AppError> {
let user = repo.get(id)?;
user.ok_or(AppError::MissingUser)
}Reflect
Why is it useful that Rust distinguishes missing data from operational failure more aggressively than JavaScript usually does?
what a strong answer notices
A strong answer mentions clearer APIs, fewer ambiguous null checks, and easier reasoning about caller responsibilities.
Rewrite
Translate this JavaScript function into Rust using `Option` or `Result` intentionally rather than relying on exceptions.
Rewrite this JavaScript/Node
function getPrimaryEmail(user) {
return user.emails[0];
}what good looks like
- Uses `Option` if absence is normal
- Uses `Result` if failure is part of the contract
- Makes the return shape more explicit than the JavaScript original
Practice
Design a Rust function that validates input, loads a record, and returns either a missing-state or an application error clearly.
success criteria
- Separates absence from failure
- Keeps the function signature honest
- Avoids recreating JavaScript's mixed null-or-throw ambiguity
Common mistakes
- Using `unwrap` where the caller should handle the case explicitly.
- Confusing missing data with operational failure.
- Trying to recreate exception-style control flow in Rust APIs.
takeaways
- ●This makes reading APIs easier. You can see whether a function can fail, whether a value can be missing, and what the caller must handle.
- ●A strong answer mentions clearer APIs, fewer ambiguous null checks, and easier reasoning about caller responsibilities.
- ●Separates absence from failure