prefect server in zig

zig 0.15 patterns#

notes from working with zig 0.15.2 in this codebase.

ArrayListUnmanaged vs ArrayList#

in zig 0.15, use ArrayListUnmanaged with explicit allocator per-method instead of ArrayList.init(alloc):

// prefer this pattern
var results = std.ArrayListUnmanaged(MyType){};
try results.append(alloc, item);
defer results.deinit(alloc);
return results.toOwnedSlice(alloc);

// NOT this (old pattern)
var results = std.ArrayList(MyType).init(alloc);  // error: no member 'init'

this matches the pattern used throughout the codebase (see db/*.zig).

pg.zig strict type checking#

postgres driver (pg.zig) is strict about column types:

  • INTEGER / INT4 → must use i32 via row.getInt(col)
  • BIGINT / INT8 → must use i64 via row.getBigInt(col)

our backend.zig abstraction:

pub fn int(self: Row, col: usize) i64 {
    // for INTEGER columns
    return switch (self) {
        .sqlite => |r| r.int(col),
        .postgres => |r| r.getInt(col),  // returns i32, coerced to i64
    };
}

pub fn bigint(self: Row, col: usize) i64 {
    // for BIGINT columns
    return switch (self) {
        .sqlite => |r| r.int(col),
        .postgres => |r| r.getBigInt(col),  // returns i64
    };
}

buffer allocation: stack first, heap fallback#

for variable-size data, try stack buffer first, fall back to heap for large data:

var heap_buf: ?[]u8 = null;
defer if (heap_buf) |hb| alloc.free(hb);

const result = blk: {
    // try 64KB stack buffer first
    var stack_buf: [65536]u8 = undefined;
    break :blk doWork(&stack_buf, data) catch |err| {
        if (err == error.NoSpaceLeft and data.len > 60000) {
            // large data - allocate on heap
            heap_buf = try alloc.alloc(u8, data.len + 1024);
            break :blk try doWork(heap_buf.?, data);
        }
        return err;
    };
};

this avoids heap allocation for common cases while handling edge cases.

function pointer types with slice parameters#

when accepting buffers, use []u8 slice instead of pointer-to-array:

// flexible - works with stack and heap buffers
fn formatJson(buf: []u8, data: []const u8) ![]const u8 {
    return std.fmt.bufPrint(buf, ...);
}

// usage with stack buffer
var stack_buf: [65536]u8 = undefined;
_ = try formatJson(&stack_buf, data);

// usage with heap buffer
const heap_buf = try alloc.alloc(u8, size);
_ = try formatJson(heap_buf, data);