# `catch`

### `catch`

`catch` handles an error at the place where it happens.

Use `try` when you want to pass the error to the caller.

Use `catch` when you want to decide what to do right now.

A function call that returns an error union has two possible outcomes:

```text
success: use the value
failure: handle the error
```

`catch` gives you the failure branch.

### Basic `catch`

Suppose we have this function:

```zig
const ParseError = error{
    InvalidDigit,
};

fn parseDigit(c: u8) ParseError!u8 {
    if (c < '0' or c > '9') {
        return error.InvalidDigit;
    }

    return c - '0';
}
```

The return type is:

```zig
ParseError!u8
```

So the call may return either an error or a `u8`.

With `catch`, we can provide a fallback value:

```zig
const digit = parseDigit('x') catch 0;
```

Read it as:

```text
try to parse the digit;
if parsing fails, use 0
```

So `digit` becomes `0`.

After this line, `digit` is a plain `u8`, not an error union.

### `catch` Must Return the Same Success Type

This matters.

The success value from `parseDigit` is a `u8`.

So the fallback after `catch` must also produce a `u8`.

This works:

```zig
const digit = parseDigit('x') catch 0;
```

This does not make sense:

```zig
const digit = parseDigit('x') catch "failed";
```

The success side gives a number. The error side gives text. Zig needs one final type for `digit`.

Both paths must fit the same result type.

### Capturing the Error

Sometimes you need to know which error happened.

Use `catch |err|`:

```zig
const digit = parseDigit('x') catch |err| {
    std.debug.print("parse failed: {}\n", .{err});
    return;
};
```

Inside the block, `err` is the error value.

For this example, `err` can only be:

```zig
error.InvalidDigit
```

In larger functions, the error set may contain many possible errors.

### Handling Errors with `switch`

A common pattern is `catch` plus `switch`.

```zig
const digit = parseDigit('x') catch |err| switch (err) {
    error.InvalidDigit => 0,
};
```

Read it as:

```text
if parsing succeeds, use the parsed digit;
if parsing fails with InvalidDigit, use 0
```

This becomes more useful when there are multiple errors.

```zig
const ParseError = error{
    EmptyInput,
    InvalidDigit,
    TooLong,
};

fn parseOneDigit(text: []const u8) ParseError!u8 {
    if (text.len == 0) {
        return error.EmptyInput;
    }

    if (text.len > 1) {
        return error.TooLong;
    }

    const c = text[0];

    if (c < '0' or c > '9') {
        return error.InvalidDigit;
    }

    return c - '0';
}
```

Now handle each error differently:

```zig
const digit = parseOneDigit(input) catch |err| switch (err) {
    error.EmptyInput => 0,
    error.InvalidDigit => 0,
    error.TooLong => 9,
};
```

This gives the program a clear local policy.

Empty input becomes `0`.

Invalid input becomes `0`.

Too-long input becomes `9`.

This is only an example. In a real parser, you might return the error upward instead.

### `catch return`

Sometimes you want to inspect the error, then return it.

```zig
const value = parseOneDigit(input) catch |err| {
    std.debug.print("failed: {}\n", .{err});
    return err;
};
```

This is similar to `try`, but with extra work before returning.

Use this when you need to log, clean up, or convert the error.

### Converting One Error into Another

A lower-level function may return errors that do not belong in your public API.

You can use `catch` to map them into your own error set.

```zig
const AppError = error{
    BadInput,
};

fn readNumber(text: []const u8) AppError!u8 {
    return parseOneDigit(text) catch |err| switch (err) {
        error.EmptyInput => error.BadInput,
        error.InvalidDigit => error.BadInput,
        error.TooLong => error.BadInput,
    };
}
```

Here, callers of `readNumber` only see:

```zig
error.BadInput
```

They do not need to know the internal details of `parseOneDigit`.

This is useful for clean API boundaries.

### `catch unreachable`

Sometimes an error is impossible because you already checked the input.

Example:

```zig
fn digitAfterCheck(c: u8) u8 {
    if (c < '0' or c > '9') {
        return 0;
    }

    return parseDigit(c) catch unreachable;
}
```

We checked that `c` is a digit before calling `parseDigit`.

So `parseDigit(c)` should not fail.

`catch unreachable` says:

```text
if this error happens, the program has reached an impossible state
```

Use this carefully. It is a claim to the compiler and to future readers. If your claim is wrong, your program has a bug.

Do not use `catch unreachable` just to silence errors.

### `catch` with a Block

The right side of `catch` can be a block.

```zig
const digit = parseOneDigit(input) catch |err| blk: {
    std.debug.print("parse error: {}\n", .{err});
    break :blk 0;
};
```

The block returns a value using `break :blk`.

Here, the fallback value is `0`.

This style is useful when handling the error needs several lines, but still produces a value.

For very simple cases, this is easier:

```zig
const digit = parseOneDigit(input) catch 0;
```

### `catch` and `void`

For a function that returns `!void`, there is no success value.

```zig
fn saveConfig() !void {
    // may fail
}
```

You can handle failure like this:

```zig
saveConfig() catch |err| {
    std.debug.print("could not save config: {}\n", .{err});
    return;
};
```

If `saveConfig` succeeds, execution continues.

If it fails, the block runs.

### `catch` vs `try`

These two lines look similar, but they mean different things.

```zig
const value = try parseOneDigit(input);
```

This means:

```text
if parsing fails, return the error to my caller
```

This line handles the error locally:

```zig
const value = parseOneDigit(input) catch 0;
```

This means:

```text
if parsing fails, use 0 and keep going
```

Use `try` when the current function does not know what to do.

Use `catch` when the current function does know what to do.

### Avoid Hiding Real Errors

A fallback can be useful, but it can also hide bugs.

This is sometimes fine:

```zig
const port = parsePort(text) catch 8080;
```

A default port may be reasonable.

This is dangerous:

```zig
const user_id = parseUserId(text) catch 0;
```

If `0` is a real user ID or a special admin ID, this fallback could cause serious mistakes.

A good fallback must be safe and intentional.

When in doubt, propagate the error with `try`.

### The Core Idea

`catch` handles an error immediately.

It lets you write:

```zig
const value = operation() catch fallback;
```

or:

```zig
const value = operation() catch |err| {
    // inspect or handle err
};
```

After `catch`, you have either the original success value or the fallback value.

That is the role of `catch`: turn an error union into a normal result by deciding what failure means at this point in the program.

