# Errors: Option and Result

> Option and Result for values that may be missing and operations that may fail, their methods, the ? operator and its rules, main returning Result, file I/O errors as values, and when to panic instead.

Source: https://docs.brinelang.org/language/error-handling/

Brine has no exceptions. A value that may be missing is an `Option[T]`, an operation that may fail returns a `Result[T, E]`, and `x?` passes a `None` or `Err` straight back to the caller. Panics are for bugs, not for failures you expect.

## Option and Result

[Section titled “Option and Result”](#option-and-result)

Both are generic enums that every program has without an import:

```brine
enum Option[T]:
    Some(value: T)
    None


enum Result[T, E]:
    Ok(value: T)
    Err(error: E)
```

Their variants are written on their own: `Some(3)`, `None`, `Ok(x)`, `Err("bad input")`. Handle them with [`match`](/language/enums-and-match/), which makes you deal with both cases:

```brine
fn find(names: [str], target: str) -> Option[int]:
    for i, name in enumerate(names):
        if name == target:
            return Some(i)
    return None


fn parse_bit(s: str) -> Result[int, str]:
    if s == "0":
        return Ok(0)
    if s == "1":
        return Ok(1)
    return Err(f"not a bit: {s}")


fn main():
    let names = ["ann", "bob"]
    match find(names, "bob"):
        case Some(i):
            print(f"bob is at {i}")
        case None:
            print("no bob")
    match parse_bit("x"):
        case Ok(bit):
            print("bit", bit)
        case Err(e):
            print("failed:", e)
```

Output

```text
bob is at 1
failed: not a bit: x
```

The error type `E` can be anything: a `str` message, an `int` code, or your own enum of failure kinds.

`None` on its own doesn’t say which `Option` it is. Where the context doesn’t either, annotate: `let n: Option[int] = None` (otherwise [E0332](/reference/errors/#e0332)).

## Methods

[Section titled “Methods”](#methods)

```brine
fn parse_bit(s: str) -> Result[int, str]:
    if s == "0":
        return Ok(0)
    if s == "1":
        return Ok(1)
    return Err(f"not a bit: {s}")


fn main():
    let good = parse_bit("1")
    let bad = parse_bit("7")
    print(good.is_ok(), good.is_err(), bad.is_err())
    print(good.unwrap(), bad.unwrap_or(-1))
    print(good.ok(), bad.ok())


    let some: Option[str] = Some("x")
    let none: Option[str] = None
    print(some.is_some(), none.is_none())
    print(some.unwrap(), none.unwrap_or("default"))
```

Output

```text
true false true
1 -1
Some(1) None
true true
x default
```

| Method                   | On             | Returns                                                  |
| ------------------------ | -------------- | -------------------------------------------------------- |
| `is_some()`, `is_none()` | `Option[T]`    | `bool`                                                   |
| `is_ok()`, `is_err()`    | `Result[T, E]` | `bool`                                                   |
| `unwrap()`               | both           | the value inside `Some`/`Ok`; **panics** on `None`/`Err` |
| `unwrap_or(default)`     | both           | the value inside, or `default`                           |
| `ok()`                   | `Result[T, E]` | `Option[T]`: `Some(value)` or `None` (drops the error)   |

That’s the complete list. To get at an error’s value, or to turn an `Option` into a `Result`, use `match`.

`unwrap()` is for when a `None` or `Err` would be a bug. If it happens, the program stops:

unwrap.br

```brine
fn main():
    let port: Option[int] = None
    print(port.unwrap())
```

Output

```text
panic: unwrap() called on None
  --> unwrap.br:3:11
```

## The ? operator

[Section titled “The ? operator”](#the--operator)

`x?` unwraps an `Option` or `Result`: it gives the value inside `Some`/`Ok`, or returns the `None`/`Err` from the current function at once.

```brine
fn parse_bit(s: str) -> Result[int, str]:
    if s == "0":
        return Ok(0)
    if s == "1":
        return Ok(1)
    return Err(f"not a bit: {s}")


fn parse_bits(text: str) -> Result[int, str]:
    var n = 0
    for c in text:
        n = n * 2 + parse_bit(c)?       # an Err is returned to the caller here
    return Ok(n)


fn first_even(xs: [int]) -> Option[int]:
    for x in xs:
        if x % 2 == 0:
            return Some(x)
    return None


fn half_first_even(xs: [int]) -> Option[int]:
    let e = first_even(xs)?             # None is returned here
    return Some(e / 2)


fn main():
    print(parse_bits("101"), parse_bits("1x1"))
    print(half_first_even([3, 8]), half_first_even([1, 3]))
```

Output

```text
Ok(5) Err("not a bit: x")
Some(4) None
```

The rules:

* `?` on an `Option` needs the function to return an `Option[...]`.
* `?` on a `Result[T, E]` needs the function to return a `Result[..., E]` with the **same** error type `E`. Errors are never converted for you.
* `?` can appear anywhere in an expression, as in `n * 2 + parse_bit(c)?`.

Breaking a rule is [E0341](/reference/errors/#e0341):

```brine
fn first_even(xs: [int]) -> Option[int]:
    for x in xs:
        if x % 2 == 0:
            return Some(x)
    return None


fn main():
    let e = first_even([1, 2])?
    print(e)
```

```text
error[E0341]: `?` needs the function to return Option[...]
 --> main.br:8:13
  |
8 |     let e = first_even([1, 2])?
  |             ^^^^^^^^^^^^^^^^^^^ this returns from a function that returns `()`
  |
  = help: change the return type to `Option[...]`, or handle the value with `match`
  = run `brine explain E0341` for more about this error
error: could not compile due to 1 error
```

```brine
fn parse_bit(s: str) -> Result[int, str]:
    if s == "1":
        return Ok(1)
    return Err(f"not a bit: {s}")


fn checked(s: str) -> Result[int, int]:
    let b = parse_bit(s)?
    return Ok(b)


fn main():
    print(checked("1"))
```

```text
error[E0341]: `?` would return a `str` error from a function whose errors are `int`
 --> main.br:7:13
  |
7 |     let b = parse_bit(s)?
  |             ^^^^^^^^^^^^^ the error here is `str`
  |
  = help: convert it with `match`, or make the function's error type match
  = run `brine explain E0341` for more about this error
error: could not compile due to 1 error
```

### Converting between error types

[Section titled “Converting between error types”](#converting-between-error-types)

Convert with `match` where the types differ. To go from a `Result` to an `Option`, use `.ok()` and then `?`:

```brine
fn parse_bit(s: str) -> Result[int, str]:
    if s == "1":
        return Ok(1)
    return Err(f"not a bit: {s}")


fn code_of(s: str) -> Result[int, int]:
    match parse_bit(s):
        case Ok(b):
            return Ok(b)
        case Err(_):
            return Err(2)


fn bit_or_none(s: str) -> Option[int]:
    let b = parse_bit(s).ok()?          # Result to Option, then `?`
    return Some(b)


fn main():
    print(code_of("1"), code_of("x"), bit_or_none("1"), bit_or_none("x"))
```

Output

```text
Ok(1) Err(2) Some(1) None
```

## main returning Result

[Section titled “main returning Result”](#main-returning-result)

`main` may return `Result[(), E]`, which lets it use `?`. An `Err` that reaches the end of `main` prints `error: `and the error to stderr, and the program exits with code 1.

## File I/O errors are values

[Section titled “File I/O errors are values”](#file-io-errors-are-values)

The file and process built-ins never crash on failure; they return a `Result` (or an `Option` or `bool`) and the failure is a value you handle:

| Built-in                 | Returns                                                                         |
| ------------------------ | ------------------------------------------------------------------------------- |
| `read_file(path)`        | `Result[str, str]`: the text (must be UTF-8), or the reason it couldn’t be read |
| `write_file(path, text)` | `Result[(), str]`                                                               |
| `file_exists(path)`      | `bool`                                                                          |
| `getenv(name)`           | `Option[str]`                                                                   |
| `run(cmd)`               | `Result[int, str]`: `Ok(exit code)`, or `Err` if it can’t start                 |

Together with `?` and a `main` that returns `Result`, a program reads like the happy path and still handles every failure:

lines.br

```brine
fn count_lines(path: str) -> Result[int, str]:
    let text = read_file(path)?
    return Ok(len(text.lines()))


fn main() -> Result[(), str]:
    write_file("notes.txt", "one\ntwo\nthree\n")?
    print(count_lines("notes.txt")?)
    match count_lines("missing.txt"):
        case Ok(n):
            print(n)
        case Err(e):
            print("skipped:", e)
    print(count_lines("settings.txt")?)
    print("not reached")
    return Ok(())
```

Output (exit code 1)

```text
3
skipped: cannot read missing.txt: No such file or directory
error: cannot read settings.txt: No such file or directory
```

The `os` module’s functions work the same way; see [the os module](/language/os-module/) and [built-ins](/language/builtins/).

## Panics vs expected failures

[Section titled “Panics vs expected failures”](#panics-vs-expected-failures)

Use `Option` and `Result` for failures a correct program must expect: missing files, bad input, a key that isn’t there. Use a panic for a bug: a state that should be impossible.

A panic stops the program with a message, the source location, and exit code 101. You can’t catch one. Panics come from:

* the language’s own checks: integer overflow, division by zero, an out-of-bounds index, an out-of-range conversion (see [values and types](/language/values-and-types/#panics-overflow-division-by-zero-bounds));
* `unwrap()` on `None` or `Err`, and parsing bad text with `int(s)` or `float(s)`;
* `assert cond`, which checks at run time and also tells the prover the condition holds afterwards (see [contracts](/proofs/contracts/));
* `panic(msg)`, which you call yourself.

average.br

```brine
fn average(xs: [float]) -> float:
    if len(xs) == 0:
        panic("average of an empty array")
    return sum(x for x in xs) / float(len(xs))


fn main():
    print(average([1.0, 2.0]))
    let none: [float] = []
    print(average(none))
```

Output

```text
1.5
panic: average of an empty array
  --> average.br:3:9
```

Better still, turn the bug into something the compiler checks: `requires len(xs) > 0` makes every caller prove the array isn’t empty, at compile time (see [contracts](/proofs/contracts/)).

## Summary

[Section titled “Summary”](#summary)

* `Option[T]` is `Some(value)` or `None`; `Result[T, E]` is `Ok(value)` or `Err(error)`. Both are always available.
* Methods: `is_some`, `is_none`, `is_ok`, `is_err`, `unwrap` (panics), `unwrap_or`, `ok`. Everything else is `match`.
* `x?` returns `None`/`Err` early; the function must return `Option[...]`, or `Result[..., E]` with the same `E` (E0341).
* `main` may return `Result[(), E]`: an `Err` prints `error: ...` and exits with 1.
* File and process built-ins return failures as values.
* Panics (exit 101) are for bugs and can’t be caught.
