about things notes.zzstoatzz.io
README.md

zig #

notes on zig, kept as idioms annotated with the release they were learned on. what changed between releases lives apart, in versions: the 0.15 format and io overhauls, the 0.16 migration, and 0.17's migration and silent changes.

language #

zig has no string type, so text is bytes and the vocabulary for characters is yours to choose. it has no traits either, so interfaces are comptime duck typing, type-returning functions, or vtables. most of the leverage is in comptime: generating types, validating sql before it runs, and knowing when the branch quota or @inComptime will bite. a struct copies by value, which decides how structs should hold strings, and a file sitting beside its directory is how modules stay navigable. testing is where tests vanish without a word: unresolved files, comptime folding, and a failed command label on suites that passed. inline assembly sits here too, because cpu kernels live or die on assembler feature checks and register budgets.

the standard library #

arraylist and hashmap both split into managed and unmanaged forms, and what matters is who owns the slice afterwards. json writes through Stringify and parses into an arena; binary wire formats (CBOR, CAR, varints) take any writer. compression decodes zstd with no vendored C, and crypto keeps ecdsa somewhere other than where you would look. logging goes through std.log and one logFn hook, and a cli touches most of what is left: stdout against stderr, broken pipes, spawning a pager.

since 0.16, everything that can block goes through std.Io: its concurrency (async against concurrent, futures, groups), its synchronization and cancellation model, the patterns for picking a backend and sizing its pools, an http client and server, and fibers, where LLVM drops a register clobber under ReleaseSafe.

memory and concurrency #

page_allocator hands out whole pages, so small allocations through it cost a page each, and a protocol reply whose slices all die together wants a per-command arena. 0.17's default init.gpa in Debug and ReleaseSafe is a SafeAllocator, whose buckets stay resident while one allocation in them lives. below std.Io sit atomics and memory ordering, which are still the right tool for an atomic rate meter read by many threads and written by one.

building #

the build system notes come mostly from reading ghostty and bun: basics (including why a build with no -Doptimize is Debug), organization for large build.zig files, dependencies, codegen, cross-compilation, and distribution.

around zig #

libraries and the C on the other side of them: sqlite through zqlite and its transactions, websockets from handshake to permessage-deflate, Lua embedded with ziglua, and the libuv handle lifetimes that hold even when uv_spawn fails.

zig's issues moved from github to codeberg. numbers up to about 25xxx are on github, and 30xxx and later are on codeberg, where the API searches them:

curl -s "https://codeberg.org/api/v1/repos/ziglang/zig/issues?state=all&q=flate"

sources #