🎓 A1Learner
🔍
0 of 46 lessons completed

Zig Allocation Errors

An allocation error is an error returned when an allocation cannot be satisfied. The most common one is OutOfMemory, which means no more memory is available.

Catch the exact error

The allocation does not fit, so the catch block names the exact error.

const std = @import("std");

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var buf: [16]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buf);
    const alloc = fba.allocator();
    const data = alloc.alloc(u8, 100) catch |err| {
        if (err == error.OutOfMemory) {
            try out.print("out of memory\n", .{});
        }
        return;
    };
    try out.print("len={d}\n", .{data.len});
}

Fall back to a smaller size

The big request fails, and the catch expression tries a smaller size instead.

const std = @import("std");

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var buf: [32]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buf);
    const alloc = fba.allocator();
    const data = alloc.alloc(u8, 100) catch try alloc.alloc(u8, 16);
    try out.print("len={d}\n", .{data.len});
}

Tell two errors apart

One function can fail two ways, and the caller answers each error differently.

const std = @import("std");

const AppError = error{ OutOfMemory, BadValue };

fn check(value: i32) AppError!i32 {
    if (value < 0) return error.BadValue;
    if (value > 100) return error.OutOfMemory;
    return value;
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const a = check(200) catch |err| switch (err) {
        error.OutOfMemory => 0,
        error.BadValue => -1,
    };
    try out.print("a={d}\n", .{a});
    const b = check(-5) catch |err| switch (err) {
        error.OutOfMemory => 0,
        error.BadValue => -1,
    };
    try out.print("b={d}\n", .{b});
}

Example

const std = @import("std");

fn trySizes(alloc: std.mem.Allocator) ![]u8 {
    const sizes = [_]usize{ 100, 50, 20 };
    for (sizes) |size| {
        const data = alloc.alloc(u8, size) catch null;
        if (data) |slice| {
            return slice;
        }
    }
    return error.OutOfMemory;
}

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    var buf: [32]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buf);
    const alloc = fba.allocator();
    const data = try trySizes(alloc);
    try out.print("len={d}\n", .{data.len});
}

Key points

  • error.OutOfMemory: error.OutOfMemory is the error returned when an allocation cannot be satisfied.
  • A fixed buffer that is full returns this error when a new allocation does not fit.
  • Catching the error lets the program fall back to a smaller request instead of stopping.
  • Fallback: a fallback is a second plan used when the first plan fails.
  • Propagating with try is the right choice when the caller can handle the failure better.
  • Matching the exact error matters when one function can fail in more than one way.
  • A program should handle every allocation failure, since the missing value cannot be used later.

The helper tries each size in turn. A failure moves to the next size. The first size that fits wins, and the program prints its length.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

A fixed buffer is full, so an allocation fails. Which error does the allocator return?