zig-expert

v2026.09.24

Expert knowledge in Zig systems programming, comptime metaprogramming, manual memory management, and C interoperability. Use when the user mentions systems programming, comptime, memory management, c interop, low level, or performance, or when the task involves Comptime Metaprogramming, C Interoperability, Installation and Setup, or Basic Memory Management.

GitHub
安装命令
npx skhub add personamanagmentlayer/zig-expert
Markdown
SKILL.md

Zig Expert

You are an expert in Zig programming language, specializing in systems programming, compile-time metaprogramming, manual memory management, and C interoperability.

Core Concepts

Memory Management

  • Allocators: Explicit allocation strategy with allocator pattern
  • No Hidden Control Flow: All memory operations are explicit
  • Defer/Errdefer: Guaranteed cleanup and error handling
  • RAII Alternative: Manual resource management with defer
  • Arena Allocators: Efficient bulk deallocation
  • Stack vs Heap: Clear distinction and control

Comptime Metaprogramming

  • Comptime Execution: Run arbitrary code at compile time
  • Generic Functions: Type-generic programming without templates
  • Type Reflection: Inspect and manipulate types at comptime
  • Code Generation: Generate functions, structs, and data
  • Inline Assembly: Low-level control when needed

Error Handling

  • Error Unions: Explicit error handling with ! operator
  • Try/Catch: Propagate or handle errors explicitly
  • Error Sets: Define possible error conditions
  • Payload Capture: Extract values from error unions
  • Switch on Errors: Pattern match on error types

C Interoperability

  • C ABI Compatibility: Direct C function calls
  • Translate-C: Automatic C header translation
  • Export to C: Export Zig functions for C consumption
  • Packed Structs: Match C memory layouts
  • Opaque Types: Work with C types safely

Code Examples

Installation and Setup

# Install Zig
# Download from https://ziglang.org/download/

# Verify installation
zig version

# Create new project
mkdir my-project && cd my-project
zig init-exe

# Build and run
zig build run

# Test
zig build test

# Release build (optimized)
zig build -Doptimize=ReleaseFast

Basic Memory Management

const std = @import("std");
const Allocator = std.mem.Allocator;

pub fn main() !void {
    // Get general purpose allocator
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Single allocation
    const value = try allocator.create(i32);
    defer allocator.destroy(value);
    value.* = 42;

    // Slice allocation
    const items = try allocator.alloc(u8, 100);
    defer allocator.free(items);

    // Dynamic array (ArrayList)
    var list = std.ArrayList(i32).init(allocator);
    defer list.deinit();
    try list.append(1);
    try list.append(2);

    // Arena allocator for bulk operations
    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit();
    const arena_allocator = arena.allocator();

    // All allocations freed at once on deinit
    _ = try arena_allocator.alloc(u8, 1000);
    _ = try arena_allocator.alloc(i32, 50);
}

Comptime Metaprogramming

const std = @import("std");

// Generic function with comptime
fn max(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

// Comptime type inspection
fn printTypeInfo(comptime T: type) void {
    const info = @typeInfo(T);
    std.debug.print("Type: {s}\n", .{@typeName(T)});

    switch (info) {
        .Struct => |s| std.debug.print("Struct with {} fields\n", .{s.fields.len}),
        .Int => |i| std.debug.print("Int: signed={}, bits={}\n", .{i.signedness == .signed, i.bits}),
        else => {},
    }
}

// Generic data structure
fn Stack(comptime T: type, comptime capacity: usize) type {
    return struct {
        items: [capacity]T = undefined,
        len: usize = 0,

        const Self = @This();

        pub fn push(self: *Self, item: T) !void {
            if (self.len >= capacity) return error.StackOverflow;
            self.items[self.len] = item;
            self.len += 1;
        }

        pub fn pop(self: *Self) ?T {
            if (self.len == 0) return null;
            self.len -= 1;
            return self.items[self.len];
        }
    };
}

// Comptime code generation
fn generateGetters(comptime T: type) type {
    const fields = @typeInfo(T).Struct.fields;
    var struct_fields: [fields.len]std.builtin.Type.StructField = undefined;

    inline for (fields, 0..) |field, i| {
        struct_fields[i] = .{
            .name = "get_" ++ field.name,
            .type = fn(T) field.type,
            .default_value = null,
            .is_comptime = false,
            .alignment = 0,
        };
    }

    return @Type(.{ .Struct = .{
        .layout = .Auto,
        .fields = &struct_fields,
        .decls = &.{},
        .is_tuple = false,
    }});
}

pub fn main() !void {
    // Use generic function
    const a = max(i32, 10, 20);
    const b = max(f64, 3.14, 2.71);

    // Use comptime-generated stack
    var stack = Stack(i32, 10){};
    try stack.push(42);
    if (stack.pop()) |value| {
        std.debug.print("Popped: {}\n", .{value});
    }

    // Comptime type inspection
    printTypeInfo(i32);
    printTypeInfo(struct { x: f32, y: f32 });
}

Error Handling Patterns

const std = @import("std");

const FileError = error{
    FileNotFound,
    PermissionDenied,
    InvalidFormat,
};

const ParseError = error{
    InvalidSyntax,
    UnexpectedToken,
};

// Error union return type
fn readConfig(path: []const u8) (FileError || ParseError)![]const u8 {
    if (path.len == 0) return FileError.FileNotFound;

    // Propagate errors with try
    const file = try std.fs.cwd().openFile(path, .{});
    defer file.close();

    // Error handling with catch
    const size = file.getEndPos() catch |err| {
        std.debug.print("Error getting file size: {}\n", .{err});
        return err;
    };

    return "config data";
}

// Errdefer for cleanup on error
fn processData(allocator: std.mem.Allocator) !void {
    const buffer = try allocator.alloc(u8, 100);
    errdefer allocator.free(buffer); // Only runs on error

    // If this fails, buffer is freed by errdefer
    if (buffer.len < 50) return error.BufferTooSmall;

    defer allocator.free(buffer); // Normal cleanup
}

// Switch on error type
fn handleError(err: anyerror) void {
    switch (err) {
        error.FileNotFound => std.debug.print("File not found\n", .{}),
        error.PermissionDenied => std.debug.print("Permission denied\n", .{}),
        else => std.debug.print("Unknown error: {}\n", .{err}),
    }
}

pub fn main() !void {
    // Handle errors with catch
    const config = readConfig("config.txt") catch |err| {
        handleError(err);
        return;
    };

    // Unwrap or default value
    const data = readConfig("missing.txt") catch "default config";
    _ = data;
}

C Interoperability

const std = @import("std");
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("stdlib.h");
    @cInclude("string.h");
});

// Export function for C
export fn zig_add(a: c_int, b: c_int) c_int {
    return a + b;
}

// Call C functions
pub fn main() !void {
    // Use C stdio
    _ = c.printf("Hello from C printf!\n");

    // C memory allocation
    const ptr = c.malloc(100);
    defer c.free(ptr);

    // C string manipulation
    const str = "Hello";
    const len = c.strlen(str);
    std.debug.print("Length: {}\n", .{len});

    // Zig wrapper around C
    const result = zigAdd(10, 20);
    std.debug.print("Result: {}\n", .{result});
}

fn zigAdd(a: i32, b: i32) i32 {
    return @as(i32, zig_add(@intCast(a), @intCast(b)));
}

// Packed struct for C compatibility
const CStruct = packed struct {
    flags: u8,
    value: u32,
    padding: [3]u8,
};

// Opaque type for C handles
const CHandle = opaque {};

extern fn c_create_handle() *CHandle;
extern fn c_destroy_handle(*CHandle) void;

Async and Concurrency

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Thread pool
    var pool: std.Thread.Pool = undefined;
    try pool.init(.{ .allocator = allocator });
    defer pool.deinit();

    // Spawn threads
    var threads = try allocator.alloc(std.Thread, 4);
    defer allocator.free(threads);

    for (threads, 0..) |*thread, i| {
        thread.* = try std.Thread.spawn(.{}, worker, .{i});
    }

    for (threads) |thread| {
        thread.join();
    }

    // Atomic operations
    var counter = std.atomic.Atomic(u32).init(0);
    _ = counter.fetchAdd(1, .SeqCst);
    const value = counter.load(.SeqCst);
    std.debug.print("Counter: {}\n", .{value});
}

fn worker(id: usize) void {
    std.debug.print("Worker {} running\n", .{id});
    std.time.sleep(std.time.ns_per_ms * 100);
}

Best Practices

Memory Management

  • Always use defer for resource cleanup
  • Prefer arena allocators for temporary allocations
  • Use errdefer for error path cleanup
  • Choose appropriate allocator for use case
  • Test with FailingAllocator to ensure proper error handling
  • Avoid global allocators when possible

Comptime Usage

  • Use comptime for zero-cost abstractions
  • Leverage type reflection for generic code
  • Generate code at compile time instead of runtime
  • Use inline for to unroll loops at comptime
  • Keep comptime functions pure and deterministic

Error Handling

  • Define specific error sets for modules
  • Use try for error propagation
  • Provide context with error returns
  • Document possible errors in function signatures
  • Prefer error unions over sentinel values

C Interoperability

  • Use @cImport for C headers
  • Export functions with export keyword
  • Match C ABI with extern and calling conventions
  • Use packed structs for C struct compatibility
  • Handle C NULL pointers safely

Code Organization

  • One type per file for clarity
  • Use pub for public API
  • Group related functions in structs (namespaces)
  • Separate comptime and runtime logic
  • Write comprehensive tests with test blocks

Anti-Patterns

Memory Anti-Patterns

  • Forgetting defer/errdefer for cleanup
  • Using fixed buffers without bounds checking
  • Mixing allocator types inconsistently
  • Leaking memory in error paths
  • Over-allocating with wrong allocator choice

Comptime Misuse

  • Doing runtime work at comptime
  • Overly complex comptime metaprogramming
  • Using comptime when runtime is clearer
  • Generating excessive code bloat
  • Non-deterministic comptime behavior

Error Handling Issues

  • Using catch unreachable without verification
  • Ignoring errors with _ =
  • Returning anyerror instead of specific errors
  • Mixing error handling strategies
  • Not documenting error conditions

C Interop Problems

  • Not checking C NULL returns
  • Mismatching calling conventions
  • Incorrect struct packing/alignment
  • Memory ownership confusion with C code
  • Not handling C error conventions

General Anti-Patterns

  • Using undefined when initialization is needed
  • Relying on undefined behavior
  • Not testing edge cases
  • Ignoring compiler warnings
  • Over-engineering simple solutions

Resources

Official Documentation

Learning Resources

Community

Tools and Libraries

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

Apache-2.0

源路径

stdlib/languages/zig-expert

默认分支

main

最新提交

79ccaa9

Tree SHA

d3a3f94