Functions that return errors

for Zig 0.15.2 Buy

Functions that return errors

Since errors in Zig are values, functions can return errors.

As we saw in Chapter 6, when we declare errors, we are actually giving names to a specific set of possible errors.

  // error type for the tower

  const Failure = error{ Leak, Ungreased, Obstruction };

This allows us to write functions that can return either a valid result or one of those errors using the syntax:

  fn has_failure(self: *const HTower) error{ Leak, Ungreased, Obstruction }!bool

or like this:

  fn has_failure(self: *const HTower) !bool

or also like this:

  fn has_failure(self: *const HTower) Failure!bool

Once finished, Manitas would spend long hours inspecting the towers. He detected and sealed leaks, greased the parts, and cleaned lime and other impurities that clogged the buckets and mechanisms. If repairs had not been made, the errors would have accumulated over time, eventually ruining the tower completely.

sys_errors.zig

const std = @import("std");

const print = std.debug.print;

// error type

const Failure = error{ Leak, Ungreased, Obstruction };

// tower from the failure point of view

const HTower = struct {

    a_failures: std.ArrayList(Failure),

    fn check_tower(self: *const HTower) bool {

        return self.a_failures.items.len == 0;

    }

    // pure function

    fn has_failure(a_failures_items: []const Failure, err_x: Failure) Failure!bool {

        for (a_failures_items) |err_fail|

            if (err_fail == err_x) return err_fail;

        return false;

    }

    fn get_random_err() Failure {

        return switch (std.crypto.random.intRangeAtMost(u2, 0, 2)) {

            0 => Failure.Leak,

            1 => Failure.Ungreased,

            2 => Failure.Obstruction,

            else => undefined,

        };

    }

    // failure simulator - method with side effects on the property

    fn create_failure(self: *HTower, o_alloc: std.mem.Allocator) !void {

        const err_fail: Failure = get_random_err();

        if (has_failure(self.a_failures.items, err_fail)) |_|

            try self.a_failures.append(

                o_alloc,

                err_fail,

            )

        else |err_x|

            print("Failure already present {}\n", .{err_x});

    }

};

inline fn get_current_time_ns() i128 {

    return std.time.nanoTimestamp();

}

const N_TOWERS = 20;

const N_DELAY = 3_000_000_000; // every 3 hours (segundos)

// main can return void or a failure

pub fn main() !void {

    const o_alloc = std.heap.page_allocator;

    var a_towers = try std.ArrayList(HTower).initCapacity(o_alloc, N_TOWERS);

    for (0..N_TOWERS) |_| {

        try a_towers.append(o_alloc, HTower{

            .a_failures = std.ArrayList(Failure).empty,

        });

    }

    // time

    var n_time = get_current_time_ns();

    while (true) {

        // check every N_DELAY nanoseconds

        if (get_current_time_ns() > (n_time + N_DELAY)) {

            n_time = get_current_time_ns();

            // some random failures occur

            for (a_towers.items) |*o_tower| {

                if (std.crypto.random.intRangeAtMost(u8, 0, 10) >= 9) {

                    try o_tower.create_failure(o_alloc);

                }

            }

            // list all errors

            for (a_towers.items, 0..) |o_tower, n_i|

                if (!o_tower.check_tower())

                    print(

                        "tower {}: {any}\n",

                        .{ n_i + 1, o_tower.a_failures.items },

                    );

            print("\n---------------------------------\n", .{});

        }

    }

}

~$ zig run sys_errors.zig

tower 6: { error.Obstruction }

tower 7: { error.Leak }

tower 8: { error.Leak }

tower 18: { error.Ungreased }

tower 20: { error.Ungreased }

---------------------------------

Dynamic lists - ArrayList
Chapter summary
© 2025 - 2026 Zen of Zig