Skip to content

result

Result is either a success or a failure that carries a reason. It is part of the prelude, so it needs no import.

The type

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

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

You construct and match the variants directly:

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.

struct Config {
    port: int,
}

fn load(text: string) -> Result<Config, string> {
    if text.parse<int>() is Some(port) {
        return Ok(Config { port: port });
    }
    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.

x? reads the value out of an Ok and hands an Err back to the caller of the function it is written in, which has to return a Result with the same error type:

fn port(text: string) -> Result<int, string> {
    if text.parse<int>() is Some(n) {
        return Ok(n);
    }
    return Err("not a port: " + text);
}

fn next_port(text: string) -> Result<int, string> {
    let p = port(text)?;
    return Ok(p + 1);
}

fn main() {
    print(next_port("8080"));
    print(next_port("http"));
}

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.

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. See option.
fn parse_port(text) {
    if text.parse<int>() is Some(n) {
        return Ok(n);
    }
    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());
}