Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Supply a default or a typed error

option.unwrap_or(default) returns a present payload or the supplied default. dictionary.get(key, default) does the same for a dictionary lookup, while dictionary.get(key) still returns an Option. Zero, False, and an empty string count as present values.

def main() -> Result[(), AllocError | IoError]:
    numbers = {"answer": 42}?
    print(numbers.get("answer", 0))?
    print(numbers.get("missing", 0))?
    missing: Option[i64] = Nothing
    print(missing.unwrap_or(9))?
    Ok(())
42
0
9

option.ok_or(error) converts absence into a typed Result. Choose an error that explains the missing value, then use ? inside a function returning a compatible Result.

enum Missing:
    Answer

def answer(numbers: &dict[str, i64]) -> Result[i64, Missing]:
    value = numbers.get("answer").ok_or(Missing.Answer)?
    Ok(value)

def main() -> Result[(), AllocError | IoError]:
    numbers = {"answer": 42}?
    match answer(&numbers):
        case Ok(value):
            print(value)?
        case Err(_):
            print("missing answer")?
    Ok(())
42

These helpers add no allocation. They evaluate their arguments eagerly, even when a value is present. The Option methods consume owned payloads and drop an unused default or error. Dictionary lookup supports copyable values; use pop to remove and take an owned value. Use match when the fallback should run only on absence, or choose a lazy helper as described below. Bare Nothing may need an Option annotation or a typed result context to determine its payload type.

Compute a default only when needed

Both Option and Result provide unwrap_or_else: it calls a fallback only for Nothing or Err. A Result fallback receives the error. Option also has ok_or_else for constructing an absence error lazily. These take function values; functions as values and closures explain callbacks in more detail.

A fallback can itself fail. In that case, map_or_else lets both branches return a Result, and ? handles that Result after selection:

def default_port() -> Result[i64, ParseError]:
    i64.parse("8080")

def port(configured: Option[i64]) -> Result[i64, ParseError]:
    Ok(configured.map_or_else(default_port, Ok)?)

def main() -> Result[(), ParseError | IoError]:
    print(port(Some(9000))?, port(Nothing)?)?
    Ok(())
9000 8080

default_port runs only for Nothing. Writing configured.unwrap_or(default_port()?) would run it even for Some(9000). A match is equally suitable when either branch needs several statements. Result.unwrap_or(default) also exists for deliberate recovery with a cheap, already available value. See defaults and recovery for ownership, evaluation order, and the Result callback’s error argument.