Zig General Purpose Allocator Patterns
A general purpose allocator is an allocator that serves many allocations and checks them for leaks when it is destroyed. It is the usual choice for long-lived programs.
Allocate and free
Every allocation from this allocator gets a matching free call.
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const alloc = gpa.allocator();
const data = try alloc.alloc(u8, 4);
data[0] = 42;
try out.print("data={d}\n", .{data[0]});
alloc.free(data);
}
Check the shutdown status
The shutdown status says clean here, since the slice was freed first.
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
const data = try alloc.alloc(u8, 2);
data[0] = 5;
data[1] = 6;
try out.print("sum={d}\n", .{ data[0] + data[1] });
alloc.free(data);
const status = gpa.deinit();
if (status == .ok) {
try out.print("clean\n", .{});
}
if (status == .leak) {
try out.print("leak\n", .{});
}
}
Backing an arena
The arena serves short-lived values, and the outer allocator checks for leaks at the end.
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var arena = std.heap.ArenaAllocator.init(gpa.allocator());
defer arena.deinit();
const alloc = arena.allocator();
const a = try alloc.alloc(u8, 3);
a[0] = 7;
try out.print("first={d}\n", .{a[0]});
}
Example
const std = @import("std");
pub fn main() !void {
const out = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
const nums = try alloc.alloc(u8, 3);
nums[0] = 4;
nums[1] = 5;
nums[2] = 6;
var total: usize = 0;
for (nums) |n| {
total += n;
}
try out.print("total={d}\n", .{total});
alloc.free(nums);
const status = gpa.deinit();
if (status == .ok) {
try out.print("clean\n", .{});
}
if (status == .leak) {
try out.print("leak\n", .{});
}
}
Key points
- GeneralPurposeAllocator: a general purpose allocator is an allocator that tracks allocations and reports leaks at shutdown.
- Deinit status: a deinit status is the value returned at shutdown, saying whether every allocation was freed.
- Every allocation from this allocator must be freed by hand.
- Leak: a leak is an allocation that is never freed.
- Discarding the status with defer is safe once the program frees everything it allocated.
- Checking the status lets a program print clean only when every allocation was freed.
- This allocator can back an arena, so short-lived arena values still get a leak check at the end.
The program allocates three numbers and adds them. It frees the slice by hand. The shutdown check then prints clean, since nothing leaked.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What must a program do for every allocation from a general purpose allocator?