Skip to content

result

Result is either a success or a failure that carries a reason.

import "std/result";

The type

Result is an ordinary candela enum with a type parameter for each side:

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Importing the module brings the variants into scope, so you construct and match them directly:

import "std/result";

fn main() {
    let r = Ok(5);
    match r {
        Ok(v) => { print(v); }
        Err(e) => { print(e); }
    }
}

Neither constructor needs a type argument: Ok(v) decides the success type and Err(e) the error type, so a function that returns both hands back a Result with each side typed, and the value bound in an arm keeps the type that went in.

import "std/result";

struct Config {
    port: int,
}

fn load(text: string) -> Result<Config, string> {
    if text.is_int() {
        return Ok(Config { port: int(text) });
    }
    return Err("not a port: " + text);
}

fn main() {
    match load("8080") {
        Ok(config) => { print(config.port); }
        Err(reason) => { print(reason); }
    }
}

A constructor names only its own side, so Ok(2) on its own is a Result<int, any> and goes wherever a Result<int, E> is expected. See enums for the enum and match syntax, and generics for type parameters.

A Result is a value you pass around and inspect. It is separate from the language's raised errors, which unwind to a try/catch; see error handling.

The module is pure candela, so it compiles into a .cdlb artifact and runs under candela-vm with no dynamic library.

Methods

The helpers are methods on the result value, defined in an impl Result<T, E> block; importing the module brings them in.

is_ok

r.is_ok()
  • Returns: a bool, true when the result is Ok.

is_err

r.is_err()
  • Returns: a bool, true when the result is Err.

unwrap

r.unwrap()
  • Returns: the success value.
  • Raises: called unwrap on an Err result when the result is Err. The message does not include the error payload; read it with unwrap_err.

unwrap_err

r.unwrap_err()
  • Returns: the error value.
  • Raises: called unwrap_err on an Ok result when the result is Ok.

unwrap_or

r.unwrap_or(default)
  • default: the value to return when the result is Err. It has the result's own success type.
  • Returns: the success value, or default.
  • Raises: nothing.

map

r.map(f)
  • f: takes the success value, returns the mapped value.
  • Returns: Ok(f(v)) for an Ok(v), and the Err unchanged. f is not called on an Err.

map_err

r.map_err(f)
  • f: takes the error value, returns the mapped error.
  • Returns: Err(f(e)) for an Err(e), and the Ok unchanged. f is not called on an Ok.

and_then

r.and_then(f)
  • f: takes the success value and returns a result of its own.
  • Returns: f(v) for an Ok(v), and the Err unchanged. f is not called on an Err. Use it to chain steps that may each fail, where map would give you a result inside a result.

ok

r.ok()
  • Returns: Some(v) for an Ok(v), and None for an Err, which drops the error. The module imports option for this, so a program that imports std/result has Some and None in scope as well, and importing both modules is fine.
import "std/result";

fn parse_port(text) {
    if text.is_int() {
        return Ok(int(text));
    }
    return Err("not a number: " + text);
}

fn main() {
    print(parse_port("8080").unwrap());
    print(parse_port("http").unwrap_or(80));
    print(parse_port("http").unwrap_err());
}