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

Error Handling

Abra uses two prelude types to represent operations that can fail: option (for “value or nothing”) and result (for “value or error”).

option

Use option<T> when the only failure mode is “no value”:

type option<T> =
  | some(T)
  | none
fn safe_divide(a: float, b: float) -> option<float> {
    if b == 0.0 {
        .none
    } else {
        .some(a / b)
    }
}

result

Use result<T, E> when failure carries information about what went wrong:

type result<T, E> =
  | ok(T)
  | err(E)

Most standard library functions that can fail return a result. For example, core/fs::read returns result<string, FsError>:

use core/fs

match read("greeting.txt") {
    .ok(contents) -> println(contents)
    .err(e) -> println("could not read: " .. e)
}

Unwrap (!)

The unwrap operator ! extracts the value from a some or ok, panicking if it’s a none or err. Use it when you know the value is present.

let a = "10".to_int()!     // panics if not a valid int
let b = "5".to_int()!
println(a + b)             // prints "15"

! is syntactic sugar for the Unwrap interface, which is implemented for both option and result.

Try (?)

The try operator ? propagates a none or err to the caller, returning early. The enclosing function must return a compatible type.

use core/fs

fn concat_files(a: string, b: string) -> result<string, FsError> {
    let first = read(a)?       // early-return if read fails
    let second = read(b)?
    .ok(first .. second)
}

If read(a) returns .err(e), concat_files returns .err(e) immediately and the rest of the function does not run.

? works with option too, but the enclosing function must return an option:

fn first_two_chars(s: string) -> option<string> {
    let chars = s.chars()
    if chars.len() < 2 {
        return .none
    }
    .some(chars[0] .. chars[1])
}

Converting between error types

When you want to bubble up an error but the error types don’t match, use result.map_err to convert:

use core/fs

fn run() -> result<string, string> {
    let contents = read("file.txt").map_err(e -> e.str())?
    .ok(contents)
}

Here read returns result<string, FsError> but run returns result<string, string>, so we convert FsError to string before propagating.