Skip to content

The ? Operator

The ? operator is syntactic sugar for automatic propagation: if the value is Result::Ok(v), it extracts v; if it is Result::Err(e), it immediately returns the error to the caller. The effect is the same as match + return, but without the visual noise.

The file below compares the verbose form with the concise form and shows that ? also works with T? (optional):

? on Result and on Option: clean propagation without manual if/match.

03-try-operator.zolo
Playground
// Feature: `?` operator — automatic propagation
// Syntax: `let x = expr?` (extracts Ok or returns Err)
// When to use: chain Result-returning calls without boilerplate.

use std::Result

fn divide(a: int, b: int) -> Result<int, str> {
  if b == 0 {
    return Result.Err("division by zero")
  }

  return Result.Ok(a / b)
}

// Without `?` — nested match, verbose:
fn compute_verbose(a: int, b: int, c: int) -> Result<int, str> {
  let r1 = divide(a, b)
  if r1.is_err() {
    return r1
  }

  let x = r1.unwrap()
  let r2 = divide(x, c)
  if r2.is_err() {
    return r2
  }

  return Result.Ok(r2.unwrap())
}

// With `?` — clean:
fn compute(a: int, b: int, c: int) -> Result<int, str> {
  let x = divide(a, b)?  // if Err, returns Err immediately
  let y = divide(x, c)?  // same here
  return Result.Ok(y)
}

fn describe(r: Result<int, str>) {
  match r {
    Result::Ok(v) => print("ok: {v}"),
    Result::Err(e) => print("error: {e}"),
  }
}

describe(compute(100, 4, 5))

// expected: ok: 5
describe(compute(100, 0, 5))

// expected: error: division by zero
describe(compute(100, 4, 0))

// expected: error: division by zero

// Verbose and short forms produce the same result:
describe(compute_verbose(100, 4, 5))  // ok: 5
describe(compute_verbose(100, 0, 5))  // error: division by zero

// `?` also works with Option (T?). If None/nil, returns nil.
fn first(arr: [int]) -> int? {
  if arr.len() == 0 {
    return nil
  }

  return arr[0]
}

fn sum_firsts(a: [int], b: [int]) -> int? {
  let x = first(a)?
  let y = first(b)?
  return x + y
}

print(sum_firsts([1, 2], [3, 4]))  // 4
print(sum_firsts([], [3, 4]))  // nil
print(sum_firsts([1], []))  // nil

The function that uses ? must have a compatible return type: Result<_, E> for Result, or T? for optionals.

Challenge

Try calling compute(100, 4, 0) and compute(0, 1, 1). Which step in the chain triggers the error in each case? Change divide to return 0 instead of Err when b == 0 and observe what changes.

enespt-br