04open 25 min

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