# Returning Errors

### Returning Errors

A function returns an error the same way it returns a normal value: with `return`.

```zig
fn fail() !void {
    return error.Failed;
}
```

The return type is `!void`, so the function may return either `void` or an error.

A more useful example:

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

fn checkName(name: []const u8) !void {
    if (name.len == 0) {
        return error.EmptyName;
    }

    if (name.len > 32) {
        return error.NameTooLong;
    }
}

pub fn main() !void {
    try checkName("zig");
    std.debug.print("name is valid\n", .{});
}
```

`checkName` has no successful value to return. It only checks the input. If the input is good, it reaches the end of the function and returns `void`.

If the input is bad, it returns an error.

```zig
return error.EmptyName;
```

or:

```zig
return error.NameTooLong;
```

The caller may propagate the error:

```zig
try checkName("zig");
```

or handle it:

```zig
checkName("") catch |err| {
    std.debug.print("bad name: {}\n", .{err});
    return;
};
```

Errors can be returned from inside branches.

```zig
fn firstByte(s: []const u8) !u8 {
    if (s.len == 0) {
        return error.EmptySlice;
    }

    return s[0];
}
```

This function returns a byte when the slice has one. It returns `error.EmptySlice` when the slice is empty.

The successful return is:

```zig
return s[0];
```

The failing return is:

```zig
return error.EmptySlice;
```

Both match the return type:

```zig
!u8
```

A function may use an explicit error set.

```zig
const NameError = error{
    EmptyName,
    NameTooLong,
};

fn checkName(name: []const u8) NameError!void {
    if (name.len == 0) {
        return NameError.EmptyName;
    }

    if (name.len > 32) {
        return NameError.NameTooLong;
    }
}
```

Now the signature says exactly which errors may come back.

```zig
NameError!void
```

This is often useful at API boundaries. A caller can read the type and know the failure cases.

Inside small private functions, inferred error sets are often enough:

```zig
fn checkName(name: []const u8) !void {
    if (name.len == 0) return error.EmptyName;
    if (name.len > 32) return error.NameTooLong;
}
```

Both styles are common. Use the explicit form when the set of errors is part of the contract.

Errors can be translated before returning.

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

fn openConfig() !std.fs.File {
    return std.fs.cwd().openFile("config.txt", .{}) catch |err| switch (err) {
        error.FileNotFound => error.MissingConfig,
        else => err,
    };
}
```

This function hides the file-system detail `FileNotFound` and returns a program-level error `MissingConfig`.

The `else => err` case returns any other error unchanged.

This pattern is useful when lower-level errors are too specific for the caller. The public function can expose errors in the language of the application.

A function may also clean up before returning an error. For that, Zig uses `defer` and `errdefer`. The next sections will cover them more carefully, but the basic idea is simple:

```zig
fn makeThing(allocator: std.mem.Allocator) ![]u8 {
    const buf = try allocator.alloc(u8, 1024);
    errdefer allocator.free(buf);

    // more work that may fail

    return buf;
}
```

If later work fails, `errdefer` frees the buffer before the error returns.

Returning errors should be direct. Do not hide failure in sentinel values like `0`, `-1`, or an empty string unless those values are truly part of the data. Use an error when the operation failed.

Exercise 8-9. Write `firstByte` for a slice of bytes.

Exercise 8-10. Write `checkAge` that returns `error.TooYoung` if the age is less than 18.

Exercise 8-11. Define an explicit error set for username validation.

Exercise 8-12. Write a function that converts `error.FileNotFound` into `error.MissingConfig`.

