Quick Reference

Zig Quick Reference

Common Patterns

Memory Management

// Allocator setup (in main)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();  // Catches leaks
const allocator = gpa.allocator();

// Allocation
const buffer = try allocator.alloc(u8, size);
defer allocator.free(buffer);

// Array vs Slice
var array: [100]u8 = undefined;     // Stack, fixed size
const slice = try allocator.alloc(u8, 100);  // Heap, dynamic

Error Handling

// Propagate errors up
const result = try mightFail();

// Handle specific error
const result = mightFail() catch |err| {
    std.debug.print("Error: {}\n", .{err});
    return err;
};

// Provide default
const result = mightFail() catch default_value;

// Conditional
if (mightFail()) |value| {
    // Success path
} else |err| {
    // Error path
}

Struct Patterns

const Thing = struct {
    data: []u8,
    allocator: std.mem.Allocator,

    // Constructor
    fn init(allocator: std.mem.Allocator, size: usize) !Thing {
        return Thing{
            .data = try allocator.alloc(u8, size),
            .allocator = allocator,
        };
    }

    // Destructor
    fn deinit(self: *Thing) void {
        self.allocator.free(self.data);
    }

    // Const method
    fn get(self: *const Thing, index: usize) u8 {
        return self.data[index];
    }

    // Mutable method
    fn set(self: *Thing, index: usize, value: u8) void {
        self.data[index] = value;
    }
};

// Usage
var thing = try Thing.init(allocator, 100);
defer thing.deinit();

ArrayList (Zig 0.15.2)

// Initialize
var list: std.ArrayList(u8) = .{};
defer list.deinit(allocator);

// Common operations
try list.append(allocator, item);
try list.appendSlice(allocator, slice);
const last = list.pop();
const item = list.items[i];

// Access
list.items       // The slice
list.items.len   // Length
list.capacity    // Allocated capacity

Slices

const str = "hello world";

str[0]        // First char
str[1..]      // From index 1 to end
str[0..5]     // First 5 chars (exclusive end)
str[str.len-1]  // Last char

// Iterate
for (str) |c| {
    // Each character
}

for (str, 0..) |c, i| {
    // Character and index
}

For Loops

// Range
for (0..10) |i| { ... }

// Slice
for (slice) |item| { ... }

// With index
for (slice, 0..) |item, i| { ... }

// Multiple at once
for (slice1, slice2) |a, b| { ... }

Format Strings

// Basic
std.debug.print("{}\n", .{value});

// Specific format
{d}     // Decimal
{x}     // Hex lowercase
{X}     // Hex UPPERCASE
{b}     // Binary
{s}     // String
{c}     // Character
{e}     // Scientific notation

// With formatting
{x:0>2}   // Hex, zero-padded to 2 chars
{d:4}     // Decimal, width 4
{s:<10}   // String, left-aligned in 10 chars

Common Builtins

@min(a, b)              // Minimum
@max(a, b)              // Maximum
@intCast(value)         // Convert integer types
@as(Type, value)        // Type coercion
@sizeOf(Type)          // Size of type
@TypeOf(value)         // Get type
@memset(slice, value)  // Fill memory
@memcpy(dest, src)     // Copy memory

Type Conversions

// Explicit casts (required!)
const a: u8 = 255;
const b: u16 = @as(u16, a);          // Widen
const c: usize = @intCast(some_int); // Convert

// String to number
const num = try std.fmt.parseInt(i32, "123", 10);

// Number to string
var buf: [100]u8 = undefined;
const str = try std.fmt.bufPrint(&buf, "{d}", .{value});

File I/O (Zig 0.15.2)

// Read entire file
const file = try std.fs.cwd().openFile("path.txt", .{});
defer file.close();

const contents = try file.readToEndAlloc(allocator, max_size);
defer allocator.free(contents);

// Write file
const file = try std.fs.cwd().createFile("path.txt", .{});
defer file.close();

try file.writeAll("content");

// Buffered I/O
var buffer: [4096]u8 = undefined;
var writer = file.writer(&buffer);
try writer.print("Value: {}\n", .{42});
try writer.flush();

Zig 0.15.2 API Changes

stdout/stderr

// Old:
const stdout = std.io.getStdOut().writer();

// New:
var buffer: [4096]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
const stdout = &stdout_writer.interface;

ArrayList

// Old:
var list = std.ArrayList(u8).init(allocator);

// New:
var list: std.ArrayList(u8) = .{};
defer list.deinit(allocator);  // Pass allocator!

Compression

// Old:
const zlib = std.compress.zlib;

// New:
const flate = std.compress.flate;
var decomp = flate.Decompress.init(&reader, .zlib, &buffer);

Sleep

// Old:
std.time.sleep(ns);

// New:
std.Thread.sleep(ns);

Common Gotchas

1. Unused Variables

const x = 5;  // Error if never used

// Fix:
_ = x;  // Explicitly discard

2. Mutable vs Const

const x = 5;
x = 10;  // Error!

var y = 5;
y = 10;  // OK

3. String Literals are const

var str = "hello";  // Type: *const [5:0]u8
str[0] = 'H';  // Error: const

// Need mutable:
var buf = [_]u8{'h', 'e', 'l', 'l', 'o'};
buf[0] = 'H';  // OK

4. Integer Types Don’t Auto-Convert

const a: u8 = 5;
const b: u16 = a;  // Error!

const b: u16 = @intCast(a);  // OK

5. Undefined Behavior is a Compile Error

var index: usize = 100;
var arr = [_]u8{1, 2, 3};
const x = arr[index];  // Runtime panic in debug, UB in release

// Zig catches at compile time when possible
const x = arr[100];  // Compile error!

Testing

test "basic test" {
    const result = add(2, 3);
    try std.testing.expectEqual(@as(i32, 5), result);
}

// Run tests:
// zig test file.zig

Build System

// build.zig
pub fn build(b: *std.Build) void {
    const exe = b.addExecutable(.{
        .name = "myapp",
        .root_source_file = b.path("src/main.zig"),
        .target = b.standardTargetOptions(.{}),
        .optimize = b.standardOptimizeOption(.{}),
    });

    b.installArtifact(exe);
}
# Commands
zig build           # Build
zig build run       # Build and run
zig build test      # Run tests
zig build --help    # See options

Philosophy Reminders

  1. No hidden control flow
  2. No hidden allocations
  3. No preprocessor, no macros
  4. If it compiles, it’s well-defined
  5. Explicit is better than implicit

Wesley Ray · blog · git · resume