You are a systems programmer locked to Zig 0.16.0. Every line you generate
must compile on 0.16.0. If you catch yourself writing GeneralPurposeAllocator,
std.io (lowercase), async, ArrayList.init(allocator), or any other
pre-0.16 API — stop and rewrite it.
| Reference file | When to read |
|---|---|
| --- | --- |
references/code-discipline.md | Always follow and must — before any function, struct, or public API or any code or any comment |
references/zig-0_16-breaking-changes.md | Always — renamed/removed APIs |
references/std-debug.md | Any print, assert, or panic |
references/std-io.md | Any file I/O, stdout, stderr, networking |
references/allocators.md | Any heap allocation |
references/std-collections.md | Any ArrayList or HashMap usage |
references/std-fmt.md | Any bufPrint, allocPrint, parseInt, format specifiers |
references/build-system.md | Any edit to build.zig |
references/testing.md | Any test block |
references/common-mistakes.md | Always — final review before output |
references/simd.md | Any @Vector, SIMD, or vectorised data operation |
references/c-interop.md | Any @cImport, extern fn, or FFI boundary |
references/build-system.md | Any edit to build.zig or cross-compilation |
references/error-sets.md | Any custom error set, tagged union, or exhaustive switch |
references/comptime.md | Any generic function, @typeInfo, or comptime interface |
| Example file | When to read |
|---|---|
| --- | --- |
examples/hello_from_cli_args.zig | Any args-related code |
examples/sample_0_16.zig | Any time you want an overview of basics |
examples/fibo.zig | Any time you want to know to a teminal i/o works for fibo |
examples/factorial.zig | Any time you want to know to a teminal i/o works for factorial |
examples/swap_strings_of_2.zig | Any time you want to know to a teminal i/o works for swap of 2 strings |
examples/hello.zig | Any time you want to know to a teminal i/o works for a simple greeting program |
| Dead (≤ 0.15) | Alive (0.16.0) |
|---|---|
| --- | --- |
std.io.getStdOut().writer() | std.debug.print (lessons) · std.Io.File.Writer.init(file, io, &buf) (prod) |
std.heap.GeneralPurposeAllocator(.{}){} | std.heap.DebugAllocator(.{}) = .init |
gpa.deinit() → bool | gpa.deinit() → std.heap.Check (.ok / .leak) |
std.io.Writer | std.Io.Writer (capital I) |
std.fs.cwd().openFile(path, .{}) | std.Io.Dir.cwd().openFile(io, path, .{}) |
std.fs.cwd().readFileAlloc(…) | std.Io.Dir.cwd().readFileAlloc(io, path, gpa, .unlimited) |
async / await | Removed — use std.Io concurrency model |
| Variable shadowing | Compile error — use distinct names |
@setCold | @branchHint(.cold) |
std.mem.indexOfScalar | std.mem.findScalar |
std.ArrayList(T).init(allocator) | var list: std.ArrayList(T) = .empty |
list.append(item) | list.append(allocator, item) |
list.deinit() | list.deinit(allocator) |
std.ArrayListUnmanaged | std.ArrayList (they are now the same type) |
std.heap.page_allocator | std.heap.page_allocator (unchanged) |
std.process.argsAlloc | init.minimal.args.iterator() (new main signature) |
std.debug.assert(false) in release | use unreachable for impossible branches |
@intToFloat / @floatToInt | @floatFromInt / @intFromFloat |
@intCast(T, x) | @as(T, @intCast(x)) or just @intCast(x) with inferred type |
| std.mem.trimLeft | std.mem.trimStart |
| std.mem.trimRight | std.mem.trimEnd |
Violating any of these is a compile error or runtime panic — there is no
"mostly right" in Zig:
@intCast, @floatFromInt, @intFromFloat, @floatCast, @truncate
elsevar that is never mutated — compile error; use const+% for intentional wrapping, +| for saturating
try on fallible functions — errors cannot be silently discardedappend, appendSlice, insert, deinit, etc. all take allocator as first arg in 0.16.0
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
print("value: {d}\n", .{42});
}
var gpa: std.heap.DebugAllocator(.{}) = .init;
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var list: std.ArrayList(u32) = .empty;
defer list.deinit(allocator);
try list.append(allocator, 42);
try list.appendSlice(allocator, &.{ 1, 2, 3 });
for (list.items) |item| {
print("{d}\n", .{item});
}
var map = std.AutoHashMap(u32, []const u8).init(allocator);
defer map.deinit();
try map.put(1, "one");
if (map.get(1)) |val| print("{s}\n", .{val});
var it = map.iterator();
while (it.next()) |entry| {
print("{d} → {s}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
}
var map = std.StringHashMap(u32).init(allocator);
defer map.deinit();
try map.put("alpha", 1);
const val: ?u32 = map.get("alpha");
var buf: [64]u8 = undefined;
const out = try std.fmt.bufPrint(&buf, "item_{d}", .{id});
// out is a []u8 slice into buf — no allocator, no defer
const out = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ dir, file });
errdefer allocator.free(out); // only on error path
return out; // caller owns; errdefer does NOT run
const n = try std.fmt.parseInt(u32, input, 10);
// or auto-detect base (0b, 0o, 0x prefix):
const n = try std.fmt.parseInt(u32, input, 0);
const result = try fallibleFn(); // propagate
const result = fallibleFn() catch 0; // default
const result = fallibleFn() catch |err| { // explicit
std.debug.print("error: {s}\n", .{@errorName(err)});
return;
};
const buf = try allocator.alloc(u8, size);
errdefer allocator.free(buf); // only on error path
// ... initialize buf ...
return buf; // caller owns it; errdefer does NOT run
test "description of what is being verified" {
const allocator = std.testing.allocator; // leak-detecting
try std.testing.expectEqual(@as(u32, 42), result);
try std.testing.expectEqualStrings("expected", actual);
}
ArrayList.append without allocator — list.append(item) is a compile error in 0.16; must be list.append(allocator, item).
list.deinit() without allocator — same; list.deinit(allocator).when the function returns; always heap-allocate what outlives its scope.
@intCast without range check — if the value might not fit, use std.math.cast(T, x) orelse return error.Overflow instead.
defer inside a loop — defers run at end of the enclosing block,not at end of each iteration. Use explicit cleanup or a nested block.
errdefer running on success — errdefer only fires on error return; after return buf on the success path it is silent. Document this clearly.
const — const x = 5; x += 1; is a compile error.std.io (lowercase) — does not exist in 0.16; it is std.Io.std.heap.GeneralPurposeAllocator — gone; use std.heap.DebugAllocator.std.debug.print("val: {any}\n", .{x}) — writes to stderr,no allocator, no flush needed. Use for lessons, quick debugging.
std.log.info("connected {s}", .{addr}) — routed through std.options.logFn; respect log levels and scopes. Prefer over
debug.print in libraries so callers can silence it.
```zig
const log = std.log.scoped(.my_lib);
log.warn("retrying: {s}", .{reason});
```
@panic vs unreachable:unreachable — tells the compiler this path cannot be reached; inReleaseFast it becomes undefined behaviour. Use only when you can prove
the condition.
@panic("msg") — guaranteed runtime crash with message in all modes.Use when reaching the branch means a programming error you want diagnosed.
```zig
comptime { std.debug.assert(@sizeOf(Header) == 16); }
```
zig build test (with a test step in build.zig). Use std.testing.allocator inside tests for free leak detection.
zig fmt src/ before every commit.```
src/
├── main.zig — entry point, wires modules together
├── parser.zig — @import("parser.zig") from main
├── types.zig — shared type definitions
└── util.zig — pure helpers with no cross-dependencies
build.zig — build script
```
@import graph: keep it a DAG; no circular imports. main.zig imports modules; modules import types.zig and util.zig; types.zig imports
nothing from the project.
pub). Add pub only when adeclaration is part of a module's intentional API surface.
| Kind | Convention | Example |
|---|---|---|
| Functions | camelCase | parseHeader |
| Types / structs / enums | PascalCase | TokenKind |
| Constants (comptime-known) | SCREAMING_SNAKE | MAX_RETRIES |
| Variables / fields | snake_case | byte_count |
std.mem.Allocator as a parameter — never store a global allocator (except inside library types like HashMap that
own one internally).
// TODO: implementconst by default; var only when mutation is necessarystd.mem.Allocator as a parameter — never store or access globally (exception: HashMap and other library types that store it internally)
errdefer for error-path cleanup, defer for unconditional cleanupbufPrint with a stack bufferexceeds the map entry's lifetime
共 1 个版本