zig

📁 nzrsky/zig-skills 📅 7 days ago
1
总安装量
1
周安装量
#51187
全站排名
安装命令
npx skills add https://github.com/nzrsky/zig-skills --skill zig

Agent 安装分布

claude-code 1

Skill 文档

Zig Language Reference (v0.15.2)

Zig evolves rapidly. Training data contains outdated patterns that cause compilation errors. This skill documents breaking changes and correct modern patterns.

Design Principles

Type-First Development

Define types and function signatures before implementation. Let the compiler guide completeness:

  1. Define data structures (structs, unions, error sets)
  2. Define function signatures (parameters, return types, error unions)
  3. Implement to satisfy types
  4. Validate at compile-time

Make Illegal States Unrepresentable

Use Zig’s type system to prevent invalid states at compile time:

  • Tagged unions over structs with optional fields — prevent impossible state combinations
  • Explicit error sets over anyerror — document exactly which failures can occur
  • Distinct types via enum(u64) { _ } — prevent mixing up IDs (user_id vs order_id)
  • Comptime validation with @compileError() — catch invalid configurations at build time

Module Structure

Larger cohesive files are idiomatic in Zig. Keep related code together — tests alongside implementation, comptime generics at file scope, visibility controlled by pub. Split files only for genuinely separate concerns. The std library demonstrates this with files like std/mem.zig containing thousands of cohesive lines.

Memory Ownership

  • Pass allocators explicitly — never use global state for allocation
  • Use defer immediately after acquiring a resource — cleanup next to acquisition
  • Name allocators by contract: gpa (caller must free), arena (bulk-free at boundary), scratch (never escapes)
  • Prefer const over var — immutability signals intent and enables optimizations
  • Prefer slices over raw pointers — bounds safety

Critical: Removed Features (0.15.x)

usingnamespace – REMOVED

// WRONG - compile error
pub usingnamespace @import("other.zig");

// CORRECT - explicit re-export
const other = @import("other.zig");
pub const foo = other.foo;

async/await – REMOVED

Keywords removed from language. Async I/O support is planned for future releases.

std.BoundedArray – REMOVED

Use std.ArrayList with initBuffer:

var buffer: [8]i32 = undefined;
var stack = std.ArrayList(i32).initBuffer(&buffer);

std.RingBuffer, std.fifo.LinearFifo – REMOVED

Use std.Io.Reader/std.Io.Writer ring buffers instead.

std.io.SeekableStream, std.io.BitReader, std.io.BitWriter – REMOVED

std.fmt.Formatter – REMOVED

Replaced by std.fmt.Alt.

Undefined Behavior Restrictions (0.15.x)

Arithmetic on undefined is now illegal. Only operators that can never trigger Illegal Behavior permit undefined as operand.

// WRONG - compile error in 0.15.x
var n: usize = undefined;
while (condition) : (n += 1) {}  // ERROR: use of undefined value

// CORRECT - explicit initialization required
var n: usize = 0;
while (condition) : (n += 1) {}

// OK - space reservation (no arithmetic)
var buffer: [256]u8 = undefined;

Critical: I/O API Rewrite (“Writergate”)

The entire std.io API changed. New std.Io.Writer and std.Io.Reader are non-generic with buffer in the interface.

Writing

// WRONG - old API
const stdout = std.io.getStdOut().writer();
try stdout.print("Hello\n", .{});

// CORRECT - new API: provide buffer, access .interface, flush
var buf: [4096]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buf);
const stdout = &stdout_writer.interface;
try stdout.print("Hello\n", .{});
try stdout.flush();  // REQUIRED!

Reading

// Reading from file
var buf: [4096]u8 = undefined;
var file_reader = file.reader(&buf);
const r = &file_reader.interface;

// Read line by line (takeDelimiter returns null at EOF)
while (try r.takeDelimiter('\n')) |line| {
    // process line (doesn't include '\n')
}

// Read binary data
const header = try r.takeStruct(Header, .little);
const value = try r.takeInt(u32, .big);

Fixed Buffer Writer (no file)

var buf: [256]u8 = undefined;
var w: std.Io.Writer = .fixed(&buf);
try w.print("Hello {s}", .{"world"});
const result = w.buffered();  // "Hello world"

Fixed Reader (from slice)

var r: std.Io.Reader = .fixed("hello\nworld");
const line = (try r.takeDelimiter('\n')).?;  // "hello" (returns null at EOF)

Removed: BufferedWriter, CountingWriter, std.io.bufferedWriter()

Deprecated: GenericWriter, GenericReader, AnyWriter, AnyReader, FixedBufferStream

New: std.Io.Writer, std.Io.Reader – non-generic, buffer in interface

Replacements:

  • CountingWriter -> std.Io.Writer.Discarding (has .fullCount())
  • BufferedWriter -> buffer provided to .writer(&buf) call
  • Allocating output -> std.Io.Writer.Allocating

Critical: Build System (0.15.x)

root_source_file is REMOVED from addExecutable/addLibrary/addTest. Use root_module:

// WRONG - removed field
b.addExecutable(.{
    .name = "app",
    .root_source_file = b.path("src/main.zig"),  // ERROR
    .target = target,
});

// CORRECT
b.addExecutable(.{
    .name = "app",
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    }),
});

Module imports changed:

// WRONG (old API)
exe.addModule("helper", helper_mod);

// CORRECT
exe.root_module.addImport("helper", helper_mod);

Libraries: addSharedLibrary → addLibrary with .linkage:

// WRONG - removed function
const lib = b.addSharedLibrary(.{ .name = "mylib", ... });

// CORRECT - unified addLibrary with linkage field
const lib = b.addLibrary(.{
    .name = "mylib",
    .linkage = .dynamic,  // or .static (default)
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/lib.zig"),
        .target = target,
        .optimize = optimize,
    }),
});

Adding dependency modules:

const dep = b.dependency("lib", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("lib", dep.module("lib"));

See std.Build reference for complete build system documentation.

Critical: Container Initialization

Never use .{} for containers. Use .empty or .init:

// WRONG - deprecated
var list: std.ArrayList(u32) = .{};
var gpa: std.heap.DebugAllocator(.{}) = .{};

// CORRECT - use .empty for empty collections
var list: std.ArrayList(u32) = .empty;
var map: std.AutoHashMapUnmanaged(u32, u32) = .empty;

// CORRECT - use .init for stateful types with internal config
var gpa: std.heap.DebugAllocator(.{}) = .init;
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);

ArrayList: Unmanaged by Default

The old std.ArrayList (with allocator stored in struct) is now std.array_list.Managed. The new std.ArrayList is unmanaged — no allocator field, pass allocator to every method:

// NEW default: unmanaged (no allocator field)
var list: std.ArrayList(u32) = .empty;
try list.append(allocator, 42);
list.deinit(allocator);

// If you want old behavior (allocator in struct), use Managed:
var list = std.array_list.Managed(u32).init(allocator);
try list.append(42);  // no allocator arg needed
list.deinit();

HashMap: Managed vs Unmanaged

Same pattern applies — unmanaged variants require allocator per operation:

// Unmanaged (no internal allocator)
var map: std.StringHashMapUnmanaged(u32) = .empty;
try map.put(allocator, "key", 42);
map.deinit(allocator);

// Managed (stores allocator internally)
var map = std.StringHashMap(u32).init(allocator);
try map.put("key", 42);
map.deinit();

Naming Changes

  • std.ArrayListUnmanaged -> std.ArrayList (Unmanaged is now default, old name deprecated)
  • std.heap.GeneralPurposeAllocator -> std.heap.DebugAllocator (GPA alias still works)

Linked Lists: Generic Parameter Removed

// WRONG - old API
const Node = std.DoublyLinkedList(MyData).Node;

// CORRECT - non-generic, use @fieldParentPtr
const MyNode = struct {
    node: std.DoublyLinkedList.Node,
    data: MyData,
};
// Access: const my_node = @fieldParentPtr("node", node_ptr);

Process API: Term is Tagged Union

// WRONG - direct field access removed
if (result.term.Exited != 0) {}

// CORRECT - pattern match
switch (result.term) {
    .exited => |code| if (code != 0) { /* handle */ },
    else => {},
}

Critical: Format Strings (0.15.x)

{f} required to call format methods:

// WRONG - ambiguous error
std.debug.print("{}", .{std.zig.fmtId("x")});

// CORRECT
std.debug.print("{f}", .{std.zig.fmtId("x")});

Format method signature changed:

// OLD - wrong
pub fn format(self: @This(), comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void

// NEW - correct
pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void

Breaking Changes (0.14.0+)

@branchHint replaces @setCold

// WRONG
@setCold(true);

// CORRECT
@branchHint(.cold);  // Must be first statement in block

@export takes pointer

// WRONG
@export(foo, .{ .name = "bar" });

// CORRECT
@export(&foo, .{ .name = "bar" });

Inline asm clobbers are typed

// WRONG
: "rcx", "r11"

// CORRECT
: .{ .rcx = true, .r11 = true }

@fence – REMOVED

Use stronger atomic orderings or RMW operations instead.

@typeInfo fields now lowercase

// WRONG - old PascalCase (compile error)
if (@typeInfo(T) == .Struct) { ... }
if (@typeInfo(T) == .Slice) { ... }
if (@typeInfo(T) == .Int) { ... }

// CORRECT - lowercase, keywords escaped with @""
if (@typeInfo(T) == .@"struct") { ... }
if (@typeInfo(T) == .@"enum") { ... }
if (@typeInfo(T) == .@"union") { ... }
if (@typeInfo(T) == .@"opaque") { ... }
if (@typeInfo(T) == .slice) { ... }
if (@typeInfo(T) == .int) { ... }
if (@typeInfo(T) == .bool) { ... }
if (@typeInfo(T) == .pointer) { ... }

// Accessing fields:
const fields = @typeInfo(T).@"struct".fields;
const tag_type = @typeInfo(T).@"enum".tag_type;

Decl Literals (0.14.0+)

.identifier syntax works for declarations:

const S = struct {
    x: u32,
    const default: S = .{ .x = 0 };
    fn init(v: u32) S { return .{ .x = v }; }
};

const a: S = .default;      // S.default
const b: S = .init(42);     // S.init(42)
const c: S = try .init(1);  // works with try

Labeled Switch (0.14.0+)

State machines use continue :label:

state: switch (initial) {
    .idle => continue :state .running,
    .running => if (done) break :state result else continue :state .running,
    .error => return error.Failed,
}

Non-exhaustive Enum Switch (0.15.x)

Can mix explicit tags with _ and else:

switch (value) {
    .a, .b => {},
    else => {},  // other named tags
    _ => {},     // unnamed integer values
}

Critical: HTTP API Reworked (0.15.x)

HTTP client/server completely restructured — depends only on I/O streams, not networking:

// Server now takes Reader/Writer interfaces, not connection directly
var recv_buffer: [4000]u8 = undefined;
var send_buffer: [4000]u8 = undefined;
var conn_reader = connection.stream.reader(&recv_buffer);
var conn_writer = connection.stream.writer(&send_buffer);
var server = std.http.Server.init(
    conn_reader.interface(),
    &conn_writer.interface,
);

Note: HTTP client API is still rapidly evolving. For stability-critical code, consider shelling out to curl.

Quick Fixes

Error Fix
no field 'root_source_file' Use root_module = b.createModule(.{...})
use of undefined value Arithmetic on undefined is now illegal — initialize explicitly
type 'f32' cannot represent integer Use float literal: 123_456_789.0 not 123_456_789
ambiguous format string Use {f} for format methods
no field 'append' on ArrayList Pass allocator: list.append(allocator, val) (unmanaged default)
expected 2 arguments, found 1 on ArrayList Add allocator param: .append(allocator, val), .deinit(allocator)
BoundedArray not found Use std.ArrayList(T).initBuffer(&buf)
GenericWriter/GenericReader Use std.Io.Writer/std.Io.Reader
missing .flush() — no output Always call try writer.flush() after writing
enum has no member named 'Struct' @typeInfo fields now lowercase: .@"struct", .slice, .int
no field named 'encode' on base64 Use std.base64.standard.Encoder.encode()
no field named 'open' on HTTP Use client.request() or client.fetch()
expected error union, found Signature Ed25519.Signature.fromBytes() doesn’t return error — remove try
addSharedLibrary not found Use b.addLibrary(.{ .linkage = .dynamic, ... })

Verification Workflow

After writing or modifying Zig code, verify with this sequence:

  1. zig build — catch compilation errors, match against Quick Fixes above
  2. zig build test — run unit tests
  3. zig build -Doptimize=ReleaseFast test — detect undefined behavior (UB checks enabled in optimized builds)

Development speed tips:

  • zig build --watch -fincremental — incremental compilation, rebuilds on file change
  • 0.15.x uses self-hosted x86_64 backend by default — ~5x faster Debug builds than LLVM

Common Pitfalls

  • Forgetting defer/errdefer cleanup — place cleanup immediately after resource acquisition
  • Using anyerror instead of specific error sets — explicit sets document failure modes
  • Ignoring error unions — handle or propagate, never discard
  • Missing errdefer after allocations in multi-step init — partial construction leaks
  • Expecting comptime side effects — comptime code is evaluated lazily
  • Unhandled integer overflow — Zig traps on overflow in debug builds
  • Missing null terminators for C strings — use :0 sentinel slices: [:0]const u8
  • Using anytype when comptime T: type works — explicit types produce clearer errors
  • Scoped loggers: always define per-module const log = std.log.scoped(.my_module); for filterable logging

Learning Resources

Production Zig codebases worth studying:

  • Bun — JS runtime (~200k+ lines), async I/O, FFI, system calls
  • Ghostty — Terminal emulator, cross-platform, GPU rendering
  • TigerBeetle — Financial DB, deterministic execution, VOPR fuzzing
  • Mach Engine — Game engine, graphics, ECS
  • Sig — Solana validator, high-performance networking

Language References

Load these references when working with core language features:

Code Style

  • Style Guide – Official Zig naming conventions (TitleCase types, camelCase functions, snake_case variables), whitespace rules, doc comment guidance, redundancy avoidance, zig fmt

Language Basics & Built-ins

  • Language Basics – Core language: types, control flow (if/while/for/switch), error handling (try/catch/errdefer), optionals, structs, enums, unions, pointers, slices, comptime, functions
  • Built-in Functions – All @ built-ins: type casts (@intCast, @bitCast, @ptrCast), arithmetic (@addWithOverflow, @divExact), bit ops (@clz, @popCount), memory (@memcpy, @sizeOf), atomics (@atomicRmw, @cmpxchgWeak), introspection (@typeInfo, @TypeOf, @hasDecl), SIMD (@Vector, @splat, @reduce), C interop (@cImport, @export)

Standard Library References

Load these references when working with specific modules:

Memory & Slices

  • std.mem – Slice search/compare, split/tokenize, alignment, endianness, byte conversion

Text & Encoding

  • std.fmt – Format strings, integer/float parsing, hex encoding, custom formatters, {f} specifier (0.15.x)
  • std.ascii – ASCII character classification (isAlpha, isDigit), case conversion, case-insensitive comparison
  • std.unicode – UTF-8/UTF-16 encoding/decoding, codepoint iteration, validation, WTF-8 for Windows
  • std.base64 – Base64 encoding/decoding (standard, URL-safe, with/without padding)

Math & Random

  • std.math – Floating-point ops, trig, overflow-checked arithmetic, constants, complex numbers, big integers
  • std.Random – PRNGs (Xoshiro256, Pcg), CSPRNGs (ChaCha), random integers/floats/booleans, shuffle, distributions
  • std.hash – Non-cryptographic hash functions (Wyhash, XxHash, FNV, Murmur, CityHash), checksums (CRC32, Adler32), auto-hashing

SIMD & Vectorization

  • std.simd – SIMD vector utilities: optimal vector length, iota/repeat/join/interlace patterns, element shifting/rotation, parallel searching, prefix scans, branchless selection

Time & Timing

  • std.time – Wall-clock timestamps, monotonic Instant/Timer, epoch conversions, calendar utilities (year/month/day), time unit constants
  • std.Tz – TZif timezone database parsing (RFC 8536), UTC offsets, DST rules, timezone abbreviations, leap seconds

Sorting & Searching

  • std.sort – Sorting algorithms (pdq, block, heap, insertion), binary search, min/max

Core Data Structures

Allocators

  • std.heap – Allocator selection guide, ArenaAllocator, DebugAllocator, FixedBufferAllocator, MemoryPool, SmpAllocator, ThreadSafeAllocator, StackFallbackAllocator, custom allocator implementation

I/O & Files

  • std.io – Reader/Writer API (0.15.x): buffered I/O, streaming, binary data, format strings
  • std.fs – File system: files, directories, iteration, atomic writes, paths
  • std.tar – Tar archive reading/writing, extraction, POSIX ustar, GNU/pax extensions
  • std.zip – ZIP archive reading/extraction, ZIP64 support, store/deflate compression
  • std.compress – Compression: DEFLATE (gzip, zlib), Zstandard, LZMA, LZMA2, XZ decompression/compression

Networking

  • std.http – HTTP client/server, TLS, connection pooling, compression, WebSocket
  • std.net – TCP/UDP sockets, address parsing, DNS resolution
  • std.Uri – URI parsing/formatting (RFC 3986), percent-encoding/decoding, relative URI resolution

Process Management

  • std.process – Child process spawning, environment variables, argument parsing, exec

OS-Specific APIs

  • std.os – OS-specific APIs: Linux syscalls, io_uring, Windows NT APIs, WASI, direct platform access
  • std.c – C ABI types and libc bindings: platform-specific types (fd_t, pid_t, timespec), errno values, socket/signal/memory types, fcntl/open flags, FFI with C libraries

Concurrency

  • std.Thread – Thread spawning, Mutex, RwLock, Condition, Semaphore, WaitGroup, thread pools
  • std.atomic – Lock-free atomic operations: Value wrapper, fetch-and-modify (add/sub/and/or/xor), compare-and-swap, atomic ordering semantics, spin loop hints, cache line sizing

Patterns & Best Practices

  • Zig PatternsLoad when writing new code or reviewing code quality. Comprehensive best practices extracted from the Zig standard library: quick patterns (memory/allocators, file I/O, HTTP, JSON, testing, build system) plus idiomatic code patterns covering syntax (closures, context pattern, options structs, destructuring), polymorphism (duck typing, generics, custom formatting, dynamic/static dispatch), safety (diagnostics, error payloads, defer/errdefer, compile-time assertions), and performance (const pointer passing)
  • Production PatternsLoad when building large-scale Zig systems or optimizing performance. Real-world patterns from Bun, Ghostty, TigerBeetle: modular build systems, CPU feature locking, pre-allocated message pools, counting allocators, SIMD with scalar fallback, intrusive linked lists, cache-line aligned SoA, work-stealing thread pools, SmolStr (15-byte SSO), comptime string maps, EnumUnionType generation, VOPR fuzzing, snapshot testing, edge-biased fuzz generation, platform abstraction facades, Objective-C bridges, opaque C wrappers with RAII, packed struct bitfields, Result union types, radix sort, tournament trees
  • MCP Server PatternsLoad when building MCP servers, LSP bridges, JSON-RPC services, or protocol translators in Zig. Patterns from zig-mcp: newline-delimited vs Content-Length transport, thread-based request correlation with ResetEvent, arena-per-request memory, child process lifecycle with pipe ownership transfer, tool registry with function pointers, std.json.Stringify for manual JSON building, lazy document sync with double-check locking, graceful degradation, auto-reconnect on crash, comptime schema generation, file URI encoding, common serialization gotchas
  • Code ReviewLoad when reviewing Zig code. Systematic checklist organized by confidence level: ALWAYS FLAG (removed features, changed syntax, API changes), FLAG WITH CONTEXT (exception safety bugs, missing flush, allocator issues), SUGGEST (style improvements). Includes migration examples for 0.14/0.15 breaking changes

Serialization

  • std.json – JSON parsing, serialization, dynamic values, streaming, custom parse/stringify
  • std.zon – ZON (Zig Object Notation) parsing and serialization for build.zig.zon, config files, data interchange

Testing & Debug

  • std.testing – Unit test assertions and utilities
  • std.debug – Panic, assert, stack traces, hex dump, format specifiers
  • std.log – Scoped logging with configurable levels and output

Metaprogramming

  • Comptime Reference – Comptime fundamentals, type reflection (@typeInfo/@Type/@TypeOf), loop variants (comptime for vs inline for), branch elimination, type generation, comptime limitations
  • std.meta – Type introspection, field iteration, stringToEnum, generic programming

Compiler Utilities

  • std.zig – AST parsing, tokenization, source analysis, linters, formatters, ZON parsing

Security & Cryptography

  • std.crypto – Hashing (SHA2, SHA3, Blake3), AEAD (AES-GCM, ChaCha20-Poly1305), signatures (Ed25519, ECDSA), key exchange (X25519), password hashing (Argon2, scrypt, bcrypt), secure random, timing-safe operations

Build System

  • std.Build – Build system: build.zig, modules, dependencies, build.zig.zon, steps, options, testing, C/C++ integration

Interoperability

  • C Interop – Exporting C-compatible APIs: export fn, C calling convention, building static/dynamic libraries, creating headers, macOS universal binaries, XCFramework for Swift/Xcode, module maps

Tooling

ZLS (Zig Language Server)

IDE support via Language Server Protocol. Provides autocomplete, go-to-definition, hover docs, diagnostics.

Version matching rule: Use ZLS release matching your Zig release (0.15.x ZLS for 0.15.x Zig). Nightly Zig needs nightly ZLS.

Installation:

# VS Code: install "Zig Language" extension (includes ZLS)

# Manual / other editors:
# Download from https://github.com/zigtools/zls/releases
# Or build from source:
git clone https://github.com/zigtools/zls
cd zls && git checkout 0.15.0  # match your Zig version
zig build -Doptimize=ReleaseSafe

# Configure:
zls --config

Editor support: VS Code, Neovim (nvim-lspconfig), Helix, JetBrains, Emacs (lsp-mode), Sublime Text, Kate.

Key features:

  • Autocomplete with semantic analysis
  • Go-to-definition, find references
  • Hover documentation
  • Diagnostics (compile errors inline)
  • Filesystem completions inside @import("") strings
  • std and builtin module path completions
  • Snippets for common declarations

anyzig (Version Manager)

Universal Zig version manager — run any Zig version from any project. Replaces manual version switching.

Source: https://github.com/marler8997/anyzig

How it works:

  1. Reads minimum_zig_version from build.zig.zon (searches up directory tree)
  2. Auto-downloads needed compiler version into global cache
  3. Invokes the correct zig transparently

Installation:

# macOS (Apple Silicon)
curl -L https://marler8997.github.io/anyzig/aarch64-macos/anyzig.tar.xz | tar xJ
mv zig /usr/local/bin/zig  # replaces/shadows system zig

# macOS (Intel)
curl -L https://marler8997.github.io/anyzig/x86_64-macos/anyzig.tar.xz | tar xJ
mv zig /usr/local/bin/zig

# Linux (x86_64)
curl -L https://marler8997.github.io/anyzig/x86_64-linux/anyzig.tar.xz | tar xJ
sudo mv zig /usr/local/bin/zig

Usage:

# Automatic — reads build.zig.zon minimum_zig_version:
cd myproject && zig build

# Manual version override:
zig 0.13.0 build-exe myproject.zig
zig 0.15.2 build

# Mach engine versions supported:
# Reads .mach_zig_version from build.zig.zon
# Format: 2024.10.0-mach

# anyzig-specific commands:
zig any --help

build.zig.zon version field:

.{
    .name = .myproject,
    .version = "0.1.0",
    .minimum_zig_version = "0.15.2",
    // ...
}