# Error handling without exceptions

There is no `try`, no `catch`, no `throw`, and no `?` operator. A fallible function returns a tagged-union value and the caller matches on it. The control flow you see is the control flow that runs.

## Define your own result type

```cplus
enum ParseResult {
    Ok(i32),
    BadInput,
    Overflow,
}

fn parse(s: str) -> ParseResult { ... }
```

## The verbose form: explicit `match`

`match` is exhaustive, so you cannot forget a case:

```cplus
fn parse_or_zero(s: str) -> i32 {
    return match parse(s) {
        ParseResult::Ok(v)       => v,
        ParseResult::BadInput    => 0 -% 1,
        ParseResult::Overflow    => 0 -% 2,
    };
}
```

## The readable form: `guard let`

`guard let` binds the happy-path value and forces you to handle the failure by leaving the scope. The rest of the function then reads straight through:

```cplus
fn handle(s: str) -> i32 {
    guard let ParseResult::Ok(v) = parse(s) else { return 0 -% 1; };
    return v +% 100;
}
```

## Generic Result and Option from the stdlib

For the common shapes, the standard library provides generic types:

```cplus
import "stdlib/result" as result;
import "stdlib/option" as option;

fn maybe_lookup(k: str) -> option::Option[i32] {
    if k == "answer" { return option::Option[i32]::Some(42); }
    return option::Option[i32]::None;
}
```

There is no `?` propagation operator and no `!T` magic. The control-flow primitives plus `guard let` give you the same ergonomics with full locality: a reader never has to look anywhere but the function in front of them to see how an error travels.
