Zig Systems Programming Agent Rules
Project Context
You are working on a systems-level project with Zig 0.13+, targeting performance-critical applications such as networking, embedded systems, compilers, or game engines.
Code Style
- Run `zig fmt` on all source files before committing — it is the single formatting authority.
- Use `snake_case` for functions, variables, and fields; use `PascalCase` for types and namespaces.
- Write doc comments (`///`) for all public declarations; include usage examples for non-trivial APIs.
- Keep functions short and focused; extract helpers when a function exceeds 60 lines.
- Prefer explicit over implicit — Zig values clarity over brevity; avoid implicit numeric conversions.
- Cache `@import("std")` in a module-level `const std = @import("std")` — never import inline in tight loops.
Memory Management
- Accept an `std.mem.Allocator` as the first parameter in every function and struct init that allocates.
- Use `std.heap.GeneralPurposeAllocator` in development to detect leaks and double-frees automatically.
- Use `std.heap.ArenaAllocator` for request-scoped or batch allocations; free the entire arena at once.
- Use `std.heap.FixedBufferAllocator` for stack-based allocation when no syscalls are acceptable.
- Pair every allocation with `defer allocator.free(ptr)` immediately after the allocation statement.
- Use `errdefer allocator.free(ptr)` for allocations that must be freed only on error exit paths.
- Prefer slices over raw pointers — slices carry length and enable bounds checking in safe build modes.
- Pass `std.testing.allocator` in tests to detect memory leaks automatically at test teardown.
Error Handling
- Use Zig's error union types (`!T`) for all fallible operations; never ignore errors.
- Use `try` to propagate errors up the call stack; use `catch` with a handler when you can meaningfully recover.
- Define specific error sets per function: `error{FileNotFound, PermissionDenied, OutOfMemory}`.
- Use `errdefer` for cleanup that must run only on error paths (freeing memory, closing handles).
- Use `defer` for cleanup that must always run regardless of outcome.
- Avoid `catch unreachable` unless you can mathematically prove the error cannot occur — document the proof.
- Use `@errorName(err)` in log messages for human-readable error names.
Comptime
- Use `comptime` for zero-cost generics: type-safe data structures, compile-time format strings, protocol parsers.
- Use `comptime` parameters instead of `anytype` when the valid type set is known and constrained.
- Use `@typeInfo`, `@Type`, and `std.meta` for type introspection and generic algorithm construction.
- Validate function parameters at `comptime` with `@compileError("reason")` for clear error messages on misuse.
- Precompute lookup tables, hash values, and configuration constants with `comptime` blocks.
- Monitor compile time; complex `comptime` code can slow builds significantly — profile with `--verbose-cimport`.
C Interop
- Use `@cImport` and `@cInclude` to import C headers; Zig translates them to Zig types automatically.
- Use `extern struct` for C-compatible struct layouts; use `packed struct` for exact binary wire formats.
- Represent C strings as `[*:0]const u8` (sentinel-terminated pointer); convert to Zig slices with `std.mem.span`.
- Use `std.heap.c_allocator` for memory that will be freed by C code via `free()`.
- Wrap unsafe C API calls in safe Zig functions that validate inputs and convert return codes to Zig errors.
- Link libraries in `build.zig` with `step.linkLibC()` and `step.linkSystemLibrary("name")`.
Build System
- Define all build logic in `build.zig`; use `build.zig.zon` for the package manifest and dependencies.
- Use `b.addExecutable`, `b.addStaticLibrary`, `b.addSharedLibrary` to define build artifacts.
- Pass options between build and runtime code with `b.addOptions()` and `exe.options.addOption(...)`.
- Use `b.addTest` and `b.step("test", "Run tests")` to integrate tests into the build graph.
- Pin dependency versions in `build.zig.zon` with a `hash` field for reproducible builds.
Testing
- Write inline tests with `test "description" { ... }` blocks co-located with the code under test.
- Use `std.testing.expect`, `std.testing.expectEqual`, and `std.testing.expectError` for assertions.
- Run with `zig build test`; configure multiple test steps in `build.zig` for unit and integration categories.
- Test error paths: verify that functions return the correct error value for invalid or unexpected inputs.
- Use `std.testing.refAllDecls(@This())` to ensure all declarations in a file are compiled and analyzed.
Performance
- Profile with `std.time.Timer` for micro-benchmarks; use `perf` or Instruments for macro analysis.
- Use `@Vector(N, T)` for SIMD operations — Zig auto-vectorizes when vector operations are used explicitly.
- Use `std.mem.alignedAlloc` for cache-line-aligned data in performance-critical data structures.
- Prefer `std.io.BufferedWriter` over unbuffered writes to avoid system call overhead per character.
- Compile with `-Doptimize=ReleaseFast` for maximum speed; use `ReleaseSafe` in production if safety checks are justified.