Skip to content
La3 Docs
Browse docs

Error Handling

Recoverable failures, Result<T>, Option<T>, propagation, and exception boundaries.

Result

Result<T> is for recoverable failures that are part of a function contract.

  • Ok(value) carries the successful value.
  • Err(message) carries a human-readable error.
  • The caller must handle, unwrap, transform, or propagate the result.
  • Use Result when the function author expects the caller to make a decision.
fn read_config(path: str) -> Result<Config> {
    let text = fs.read(path)?
    let cfg = json.decode::<Config>(text)?
    Ok(cfg)
}

Propagation with `?`

? keeps the happy path visible while preserving explicit failure flow.

Applied to Result, ? unwraps Ok(v) into v and returns Err(e) from the current function. Applied to Option, it unwraps Some(v) and returns absence on None. It only makes sense inside a function whose return type can represent that early exit.

Option

Option<T> is the explicit API-facing form of absence.

  • Use Option<T> in signatures when absence is part of the contract.
  • Use T | nil when local flow benefits from the lighter spelling.
  • Use map, unwrap_or, and ? to avoid noisy one-arm matches.
  • Use a full match when both absence and presence need meaningful behaviour.

Exception boundary

try and catch are for external systems that throw instead of returning values.

The language keeps value-based errors and exception-style failures separate. Inside La3 code, failures should normally be Result. At the boundary with a database, network library, operating system, or foreign code that throws, try and catch mark the translation point.

  • Typed catches handle expected external failures.
  • A final untyped catch can report or convert unexpected failures.
  • finally is for cleanup that must happen regardless of outcome.