Xo is experimental. The source code is not public yet; it will be soon. Read the release notes
Menu · Errors you have to handle

Docs

Errors you have to handle

A function that can fail says so after !, and its caller gets a Result. Errors are ordinary values, usually an enum.

errors/main.xo

enum ConfigErr {
  NotANumber(s: Str)
  OutOfRange(n: Int)
}

fn (e: ConfigErr) display() -> Str {
  match e {
    NotANumber(s) => "not a number: ${s}"
    OutOfRange(n) => "out of range: ${n}"
  }
}

fn parse_port(s: Str) -> Int ! ConfigErr {
  let n = Int.parse(s).or_err(ConfigErr.NotANumber(s))?
  if n < 1 || n > 65535 {
    return Err(ConfigErr.OutOfRange(n))
  }
  n
}

enum StartErr {
  Config(from ConfigErr)
}

fn start(port: Str) -> Str ! StartErr {
  let p = parse_port(port)?
  "listening on ${p}"
}

fn main(os: Os) {
  for input in ["8080", "abc", "99999"] {
    match start(input) {
      Ok(msg) => os.stdio.println(msg)
      Err(Config(e)) => os.stdio.println("error: ${e.display()}")
    }
  }
  let fallback = parse_port("x").ignore_err("the default port is fine") ?? 8080
  os.stdio.println("fallback ${fallback}")
}

Output:

listening on 8080
error: not a number: abc
error: out of range: 99999
fallback 8080
  • -> Int ! ConfigErr returns either an Int or a ConfigErr.
  • expr? returns the error to the caller. Here ? in start turns a ConfigErr into a StartErr through the from variant.
  • Int.parse(s) returns Int?; .or_err(...) turns a missing value into an error.
  • match on the result handles both outcomes.

Ignoring an error is a compile error, not a lint:

fn parse_port(s: Str) -> Int ! Error {
  Int.parse(s).or_err("not a number")?
}

fn main(os: Os) {
  parse_port("8080")
}

xo check says:

main.xo:6:3: error XO0201: result of `parse_port` is not handled
  note: `parse_port` can fail with `Error`; propagate with `?`, match on it, or discard it with `.ignore_err("reason")` (core 2.6)
  fix: discard with a reason
1 module(s), 1 error(s), 0 warning(s)

When a failure really does not matter, say why: parse_port(s).ignore_err("the default port is fine"). The reason is in the code for every reviewer to see.