🎓 A1Learner
🔍
0 of 55 lessons completed

Zig Allocators

An allocator is the part of a program that reserves memory while the program runs and takes it back when asked. Zig code receives an allocator from its caller instead of hiding where the memory comes from.

alloc reserves a slice of three numbers

alloc.alloc(i32, 3) returns a slice with room for three i32 values. The items start unset, so the code fills all three before printing.

const std = @import("std");

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const alloc = std.heap.page_allocator;
    const buf = try alloc.alloc(i32, 3);
    defer alloc.free(buf);
    buf[0] = 4;
    buf[1] = 8;
    buf[2] = 15;
    try out.print("{any}\n", .{buf});
}

Allocated bytes can hold text

Allocating u8 items gives bytes the program can fill one by one. Printing the slice with {s} shows the bytes as text.

const std = @import("std");

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const alloc = std.heap.page_allocator;
    const buf = try alloc.alloc(u8, 5);
    defer alloc.free(buf);
    buf[0] = 'H';
    buf[1] = 'e';
    buf[2] = 'l';
    buf[3] = 'l';
    buf[4] = 'o';
    try out.print("{s}\n", .{buf});
}

A loop can fill every reserved item

The slice knows its own length, so a range loop can visit each index. Each item gets ten times its index.

const std = @import("std");

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const alloc = std.heap.page_allocator;
    const buf = try alloc.alloc(i32, 4);
    defer alloc.free(buf);
    for (0..buf.len) |i| {
        buf[i] = @intCast(i * 10);
    }
    try out.print("len = {d}\n", .{buf.len});
    try out.print("{any}\n", .{buf});
}

Example

const std = @import("std");

pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const alloc = std.heap.page_allocator;
    const values = try alloc.alloc(i32, 4);
    defer alloc.free(values);
    values[0] = 3;
    values[1] = 1;
    values[2] = 4;
    values[3] = 1;
    var total: i32 = 0;
    for (values) |v| {
        total += v;
    }
    try out.print("{any}\n", .{values});
    try out.print("total = {d}\n", .{total});
}

Key points

  • Allocator: a value that hands out memory and accepts it back.
  • Page allocator: std.heap.page_allocator asks the operating system for memory a page at a time.
  • Getting one: const alloc = std.heap.page_allocator; names the page allocator used in this course.
  • alloc: alloc.alloc(i32, 3) reserves room for three i32 values and returns them as a []i32 slice.
  • free: alloc.free(buf) gives the memory of buf back to the allocator.
  • Ownership: the code that allocates a slice is responsible for freeing it.

In this example, the page allocator reserves room for four i32 values and defer lines up the matching free. The code stores 3, 1, 4, and 1, then a for loop adds every item into total. The prints show the filled slice and the total, 9.

Note

Zig has no garbage collector, so every allocation needs a matching free.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does try alloc.alloc(i32, 3) hand back when it succeeds?