# Error Unions

### Error Unions

An error union combines a normal value with an error set.

The type:

```zig
ReadError!u32
```

means:

> either a `u32`, or an error from `ReadError`.

This is the fundamental error-handling type in Zig.

A function that cannot fail returns a normal type:

```zig
fn square(x: u32) u32 {
    return x * x;
}
```

A function that may fail returns an error union:

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

    return c - '0';
}
```

The caller must consider both outcomes.

The returned value cannot be used directly:

```zig
const n = parseDigit('7');
```

because `n` is not a `u8`.

Its type is:

```zig
!u8
```

The value must first be unwrapped.

The most common way is `try`:

```zig
const n = try parseDigit('7');
```

If the function succeeds, `n` receives the `u8`.

If the function fails, the error is returned from the current function.

This produces direct and compact code:

```zig
const std = @import("std");

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

    return c - '0';
}

fn parsePair(a: u8, b: u8) !u8 {
    const x = try parseDigit(a);
    const y = try parseDigit(b);

    return x * 10 + y;
}

pub fn main() !void {
    const n = try parsePair('4', '2');
    std.debug.print("{d}\n", .{n});
}
```

The flow is straightforward:

1. call `parseDigit`
2. if it fails, return the error
3. otherwise continue

No hidden control flow is involved.

The error union may also be handled explicitly with `catch`.

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

If parsing fails, the expression becomes `0`.

This is similar to:

```zig
const n = if (parse succeeds) value else 0;
```

A larger example:

```zig
const std = @import("std");

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

    return c - '0';
}

pub fn main() void {
    const a = parseDigit('8') catch 0;
    const b = parseDigit('x') catch 0;

    std.debug.print("{d} {d}\n", .{ a, b });
}
```

The output is:

```text
8 0
```

`catch` may also introduce a named error value:

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

The variable `err` holds the error value.

Error unions work naturally with control flow because they are expressions.

This function:

```zig
fn maybeRead(ok: bool) !u32 {
    if (!ok) {
        return error.ReadFailed;
    }

    return 123;
}
```

returns one of two possibilities:

| Result | Meaning |
|---|---|
| `123` | success |
| `error.ReadFailed` | failure |

The compiler ensures that both cases are considered.

An error union occupies enough space to store either form. Zig handles the representation automatically.

Error unions are used throughout the standard library. File operations, allocation, parsing, networking, formatting, and process control all return error unions.

This keeps failure visible in the type system.

Exercise 8-5. Write a function that parses a lowercase letter and returns `error.InvalidLetter` for any other byte.

Exercise 8-6. Write a function that returns the first byte of a slice or `error.EmptySlice`.

Exercise 8-7. Rewrite a program from an earlier chapter so that it uses `try`.

Exercise 8-8. Write a function that divides two integers and uses `catch` to return zero on division failure.

