376 lines
14 KiB
Markdown
376 lines
14 KiB
Markdown
---
|
|
name: zig
|
|
description: Up-to-date Zig programming language patterns for version 0.16.0. Use when writing, reviewing, or debugging Zig code, working with build.zig/build.zig.zon, translating C headers, or using comptime metaprogramming. Critical for avoiding outdated patterns from training data, especially std.Io ownership, Juicy Main, std.Io.Dir/File/net/process APIs, Io synchronization primitives, removed @Type, @cImport migration, unmanaged containers, and removed std.Thread.Pool.
|
|
---
|
|
|
|
# Zig Language Reference (v0.16.0)
|
|
|
|
Zig evolves rapidly. Training data contains outdated patterns that cause compilation errors or subtle runtime bugs. This skill documents modern Zig 0.16.0 patterns and points to deeper references when needed.
|
|
|
|
Always load **[Zig 0.16 Release Notes Migration Reference](references/zig-0.16-release-notes.md)** for upgrade work, stdlib API questions, C translation changes, I/O changes, or code review involving old Zig examples. It is grounded in the official Zig 0.16.0 release notes: https://ziglang.org/download/0.16.0/release-notes.html
|
|
|
|
## Critical: I/O Is an Interface
|
|
|
|
Zig 0.16.0 routes many new blocking, OS-facing, and nondeterministic APIs
|
|
through a `std.Io` instance. For new APIs, treat entropy, time, networking,
|
|
process, and file operations as `std.Io` responsibilities. Existing stdlib APIs
|
|
are not uniformly parameterized this way, so check each installed signature.
|
|
|
|
Preferred app entry:
|
|
|
|
```zig
|
|
const std = @import("std");
|
|
|
|
pub fn main(init: std.process.Init) !void {
|
|
const gpa = init.gpa;
|
|
const io = init.io;
|
|
|
|
const args = try init.minimal.args.toSlice(init.arena.allocator());
|
|
_ = .{ gpa, io, args };
|
|
}
|
|
```
|
|
|
|
Preferred test I/O:
|
|
|
|
```zig
|
|
const io = std.testing.io;
|
|
```
|
|
|
|
Temporary adapter only when a boundary cannot yet receive `io`:
|
|
|
|
```zig
|
|
var threaded: std.Io.Threaded = .init_single_threaded;
|
|
const io = threaded.io();
|
|
```
|
|
|
|
General rule:
|
|
|
|
- Accept `io: std.Io` where the API performs or later owns I/O.
|
|
- Store `io` on long-lived systems, allocators, registries, timers, queues, and clients that need it after initialization.
|
|
- Do not create local fallback `Io.Threaded` instances deep in library code unless explicitly approved as a transition shim.
|
|
- When converting existing code, prefer threading `io` through signatures over global state.
|
|
|
|
## Critical: File, Net, Process, Time, Random
|
|
|
|
### Files
|
|
|
|
Use `std.Io.Dir` and `std.Io.File`; old `std.fs.File`/`std.fs.Dir` patterns are obsolete.
|
|
|
|
```zig
|
|
const cwd = std.Io.Dir.cwd();
|
|
const file = try cwd.openFile(io, "data.txt", .{});
|
|
defer file.close(io);
|
|
|
|
var buf: [4096]u8 = undefined;
|
|
var reader = file.reader(io, &buf);
|
|
const bytes = try reader.interface.allocRemaining(gpa, .limited(1024 * 1024));
|
|
defer gpa.free(bytes);
|
|
```
|
|
|
|
```zig
|
|
const file = try std.Io.Dir.cwd().createFile(io, "out.txt", .{});
|
|
defer file.close(io);
|
|
|
|
var buf: [4096]u8 = undefined;
|
|
var writer = file.writer(io, &buf);
|
|
try writer.interface.print("hello {s}\n", .{"world"});
|
|
try writer.interface.flush();
|
|
```
|
|
|
|
Convenience:
|
|
|
|
```zig
|
|
const bytes = try std.Io.Dir.cwd().readFileAlloc(io, "data.txt", gpa, .limited(max_size));
|
|
defer gpa.free(bytes);
|
|
|
|
try std.Io.Dir.cwd().writeFile(io, .{
|
|
.sub_path = "out.txt",
|
|
.data = "hello\n",
|
|
});
|
|
```
|
|
|
|
### Reader and Writer
|
|
|
|
`std.io` is replaced by `std.Io`. `GenericReader`, `AnyReader`, `GenericWriter`, `AnyWriter`, and `FixedBufferStream` are gone.
|
|
|
|
```zig
|
|
var r: std.Io.Reader = .fixed("a\nb\n");
|
|
while (try r.takeDelimiter('\n')) |line| {
|
|
_ = line;
|
|
}
|
|
|
|
var out_buf: [256]u8 = undefined;
|
|
var w: std.Io.Writer = .fixed(&out_buf);
|
|
try w.print("value={d}", .{42});
|
|
const written = w.buffered();
|
|
```
|
|
|
|
Custom format methods use the writer-only signature:
|
|
|
|
```zig
|
|
pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
|
try writer.print("{s}:{d}", .{ self.name, self.value });
|
|
}
|
|
```
|
|
|
|
Use `{f}` to invoke format methods.
|
|
|
|
### Process
|
|
|
|
Use Juicy Main for args/env and `std.process.run` / `std.process.spawn` for child processes.
|
|
|
|
```zig
|
|
const result = try std.process.run(gpa, io, .{
|
|
.argv = &.{ "git", "status", "--short" },
|
|
.stdout_limit = .limited(64 * 1024),
|
|
.stderr_limit = .limited(64 * 1024),
|
|
});
|
|
defer gpa.free(result.stdout);
|
|
defer gpa.free(result.stderr);
|
|
```
|
|
|
|
```zig
|
|
var child = try std.process.spawn(io, .{
|
|
.argv = &.{ "tool", "--flag" },
|
|
.stdout = .pipe,
|
|
.stderr = .pipe,
|
|
});
|
|
defer child.kill(io);
|
|
const term = try child.wait(io);
|
|
_ = term;
|
|
```
|
|
|
|
Current directory:
|
|
|
|
```zig
|
|
const cwd = try std.process.currentPathAlloc(io, gpa);
|
|
defer gpa.free(cwd);
|
|
```
|
|
|
|
### Networking and HTTP
|
|
|
|
Use `std.Io.net` and pass `io` to high-level clients.
|
|
|
|
```zig
|
|
var client: std.http.Client = .{
|
|
.allocator = gpa,
|
|
.io = io,
|
|
};
|
|
defer client.deinit();
|
|
```
|
|
|
|
### Time
|
|
|
|
0.16 routes runtime-dependent time through `std.Io`. Choose an explicit
|
|
`std.Io.Clock` and pass `io`; for example, use
|
|
`std.Io.Timestamp.now(io, .real)` for wall time and `.boot` or `.awake` for
|
|
elapsed-time measurements. `std.Io.Timestamp` is not a one-for-one timer
|
|
replacement: preserve the old callsite's clock semantics during migration.
|
|
|
|
Applications may centralize timestamp reads behind a shared helper so clock selection and timestamp semantics remain consistent.
|
|
|
|
### Random and Entropy
|
|
|
|
Do not use `std.crypto.random` directly in new 0.16 code.
|
|
|
|
```zig
|
|
var seed: [32]u8 = undefined;
|
|
io.random(&seed);
|
|
|
|
const source: std.Random.IoSource = .{ .io = io };
|
|
const rng = source.interface();
|
|
```
|
|
|
|
## Critical: I/O Synchronization
|
|
|
|
Blocking synchronization moved from `std.Thread` to `std.Io` so it cooperates with the runtime backend and cancelation.
|
|
|
|
Migration map:
|
|
|
|
- `std.Thread.Mutex` -> `std.Io.Mutex`
|
|
- `std.Thread.Condition` -> `std.Io.Condition`
|
|
- `std.Thread.Semaphore` -> `std.Io.Semaphore`
|
|
- `std.Thread.RwLock` -> `std.Io.RwLock`
|
|
- `std.Thread.ResetEvent` -> `std.Io.Event`
|
|
- `std.Thread.WaitGroup` -> `std.Io.Group`
|
|
- `std.Thread.Futex` operations -> `io.futexWait`, `io.futexWaitTimeout`,
|
|
`io.futexWaitUncancelable`, and `io.futexWake` on a suitably aligned
|
|
four-byte value
|
|
|
|
Cancelable lock:
|
|
|
|
```zig
|
|
try mutex.lock(io);
|
|
defer mutex.unlock(io);
|
|
```
|
|
|
|
Uncancelable lock for short critical sections or cleanup:
|
|
|
|
```zig
|
|
mutex.lockUncancelable(io);
|
|
defer mutex.unlock(io);
|
|
```
|
|
|
|
Do not replace these with custom spin loops. Use std primitives unless a measured, documented special case requires otherwise.
|
|
|
|
## Critical: Language and Builtins
|
|
|
|
### Removed or Deprecated
|
|
|
|
- `@Type` removed. Use `@Int`, `@Struct`, `@Union`, `@Enum`, `@Pointer`,
|
|
`@Fn`, `@Tuple`, or `@EnumLiteral`. Declare opaque types with `opaque {}`.
|
|
- `@cImport` is deprecated long-term. Prefer `b.addTranslateC(...)` and import `translate_c.createModule()`.
|
|
- `async`/`await` keywords remain removed; use `std.Io` task APIs, an application scheduler, or explicit threads.
|
|
- `usingnamespace` is removed; explicitly re-export names.
|
|
- `@fence` is removed; use stronger atomic orderings or RMW operations.
|
|
- `@intFromFloat` converts a finite, in-range float to an integer. `@trunc`
|
|
truncates a floating value while retaining a floating result type.
|
|
|
|
### Packed and Extern Layout
|
|
|
|
- Packed structs/unions cannot contain pointers.
|
|
- Packed unions need unambiguous bit sizes and may use explicit backing integers.
|
|
- Extern enum/packed types need explicit backing types.
|
|
- ABI-sensitive bindings must keep comptime size/alignment/offset assertions.
|
|
|
|
### Vectors and Arrays
|
|
|
|
- Runtime vector indexes are forbidden.
|
|
- Vectors and arrays no longer support in-memory coercion; convert explicitly.
|
|
|
|
### Safer Pointer Returns
|
|
|
|
The compiler rejects trivially returning the address of an expired local. When returning pointers, prove ownership and lifetime explicitly.
|
|
|
|
## Critical: Containers and Allocators
|
|
|
|
Use `.empty` for empty unmanaged containers and pass allocators to methods.
|
|
|
|
```zig
|
|
var list: std.ArrayList(u32) = .empty;
|
|
defer list.deinit(gpa);
|
|
try list.append(gpa, 42);
|
|
```
|
|
|
|
0.16 container changes:
|
|
|
|
- `std.ArrayHashMap`, `std.AutoArrayHashMap`, and `std.StringArrayHashMap` removed.
|
|
- Use `std.array_hash_map.Custom`, `std.array_hash_map.Auto`, and `std.array_hash_map.String`.
|
|
- `std.PriorityQueue` and `std.PriorityDequeue` no longer store allocators.
|
|
- Priority queues use `push`/`pop` terminology and `.empty` initialization.
|
|
- `std.SegmentedList` removed.
|
|
- `std.heap.ThreadSafe` removed.
|
|
- `std.heap.ArenaAllocator`'s allocator interface is thread-safe when its child
|
|
allocator is thread-safe. Do not assume a universal lock-free guarantee.
|
|
|
|
## Critical: Build System
|
|
|
|
The 0.15 root-module build style remains the baseline:
|
|
|
|
```zig
|
|
const exe = b.addExecutable(.{
|
|
.name = "app",
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("src/main.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
}),
|
|
});
|
|
```
|
|
|
|
Use module-level APIs:
|
|
|
|
```zig
|
|
exe.root_module.addImport("helper", helper_mod);
|
|
exe.root_module.linkSystemLibrary("SDL3", .{});
|
|
exe.root_module.addCSourceFiles(.{ .files = &.{"src/foo.c"} });
|
|
```
|
|
|
|
0.16 additions and removals:
|
|
|
|
- `--prominent-compile-errors` removed; use `--error-style minimal`.
|
|
- New `--multiline-errors` styles are available.
|
|
- `zig build test --test-timeout <duration>` can bound test runtime.
|
|
- `Build.makeTempPath` and RemoveDir step removed.
|
|
- Use `b.addTempFiles`, `b.addMutateFiles`, `b.tmpPath`, and WriteFile temp/mutate modes.
|
|
- Use build-system `addTranslateC` for new translated C bindings.
|
|
|
|
## Critical: C Translation and ABI Safety
|
|
|
|
Translate-c is now Aro/translate-c based rather than libclang based. It is intended to be non-breaking, but subtle generated-code changes can happen.
|
|
|
|
For ABI-sensitive C bindings:
|
|
|
|
- Keep translated output under review.
|
|
- Preserve size/alignment/offset comptime asserts.
|
|
- Compare enum values, bit flags, packed layouts, extern structs, calling conventions, and macro translations.
|
|
- Do not dismiss translate-c diffs as cosmetic until ABI checks pass.
|
|
|
|
## Application Integration Guidance
|
|
|
|
When integrating these patterns into an application:
|
|
|
|
- Prefer an explicit `std.Io` parameter; an application-owned accessor is also reasonable where the architecture already provides one.
|
|
- New file operations require an `io` parameter at the API boundary.
|
|
- Long-lived allocators/registries/queues that lock or perform I/O should accept `io` at init and expose it if needed.
|
|
- Prefer a shared application helper for common wall-clock timestamps.
|
|
- Keep dynamic-library behavior behind a platform abstraction, especially on Windows where `std.DynLib` support changed.
|
|
|
|
## Import Loop Guidance
|
|
|
|
Do not over-index on dependency loops when importing source files directly.
|
|
|
|
- `@import("mything.zig")` is a direct file import and should be treated as safe.
|
|
- Only consider import-loop risk when importing a module name such as `@import("module")`.
|
|
- A top-level file plus same-named helper directory is normal:
|
|
|
|
```zig
|
|
// module.zig
|
|
const moduleComponent = @import("module/moduleComponent.zig");
|
|
|
|
// module/moduleComponent.zig
|
|
const module = @import("../module.zig");
|
|
```
|
|
|
|
## Quick Fixes
|
|
|
|
| Symptom | Zig 0.16 Fix |
|
|
|---------|--------------|
|
|
| missing `std.fs.File` / `std.fs.Dir` | Use `std.Io.File` / `std.Io.Dir` and pass `io` |
|
|
| file `close()` takes an argument | Use `file.close(io)` or `dir.close(io)` |
|
|
| old stdout writer fails | `std.Io.File.stdout().writer(io, &buf)` |
|
|
| old `std.process.Child.run` fails | Use `std.process.run(gpa, io, .{ ... })` |
|
|
| old `Child.init` fails | Use `std.process.spawn(io, .{ ... })` |
|
|
| old `std.crypto.random` fails | Use `io.random` or `std.Random.IoSource` |
|
|
| old `std.Thread.Mutex.lock()` fails in I/O code | Use `std.Io.Mutex` and `lock(io)` / `lockUncancelable(io)` |
|
|
| `std.Thread.Pool` missing | Use an application scheduler or carefully designed `std.Io.Group` / `io.async` |
|
|
| `@Type` missing | Use specific type-constructor builtins |
|
|
| `@cImport` warning | Move translation to `b.addTranslateC(...)` |
|
|
| `std.mem.indexOf` not found | Use `std.mem.find` / `findScalar` / `cut` helpers |
|
|
| `{D}` duration format fails | Format `std.Io.Duration` with `{f}` |
|
|
| `std.fmt.Formatter` missing | Use `std.fmt.Alt` |
|
|
| `std.fmt.format` missing | Use `std.Io.Writer.print` |
|
|
| `std.fs.path.relative` signature changed | Pass cwd path and optional environment map |
|
|
| `stat.atime` type changed | Handle optional access time |
|
|
|
|
## References
|
|
|
|
Load these selectively:
|
|
|
|
- **[Zig 0.16 Release Notes Migration Reference](references/zig-0.16-release-notes.md)** - First stop for version-specific upgrade work, organized by official release-note section.
|
|
- **[std.Io](references/std-io.md)** - 0.16 reader/writer, file I/O, `Io` ownership, sync and task concepts.
|
|
- **[std.fs / std.Io.Dir](references/std-fs.md)** - File system migration and common 0.16 patterns.
|
|
- **[std.process](references/std-process.md)** - Juicy Main, args/env, child process APIs, current directory.
|
|
- **[std.Thread / std.Io sync](references/std-thread.md)** - Thread spawning plus migration to `std.Io` synchronization primitives.
|
|
- **[std.time / std.Io time](references/std-time.md)** - Timestamp migration and duration handling.
|
|
- **[std.Build](references/std-build.md)** - Build graph, modules, package deps, 0.16 build-system changes.
|
|
- **[Built-in Functions](references/builtins.md)** - Builtin syntax and 0.16 `@Type` replacement.
|
|
- **[Comptime Reference](references/comptime.md)** - Type reflection, type construction, lazy type resolution, compile-time patterns.
|
|
- **[C Interop](references/c-interop.md)** - C ABI, exports, translated headers, and binding checks.
|
|
- **[Zig Patterns](references/patterns.md)** - Idiomatic patterns. Treat 0.16 release-note reference as authoritative when examples conflict.
|
|
- **[Code Review](references/code-review.md)** - Review checklist. Always apply the 0.16 release-note checks first.
|
|
|
|
General references remain available for specific modules: `std.mem`, `std.fmt`, `std.json`, `std.zon`, `std.crypto`, `std.http`, `std.Io.net`, `std.compress`, `std.tar`, `std.zip`, `std.testing`, `std.debug`, `std.log`, `std.atomic`, `std.simd`, and other files under `references/`.
|
|
|
|
If any reference still contains a historical 0.14/0.15 example, the Zig 0.16 release-notes reference and the top-level rules in this file win.
|