Zig from zero — pitched at Rust developers. CLI tools, an HTTP server in
30 lines, a SQLite-backed WebSocket service, and an AWS Lambda — each
demo paired with the Rust pattern it replaces (allocators vs lifetimes,
!T vs Result<T, E>, comptime vs traits, defer/errdefer vs
Drop).
Companion code for Course 12 of the Rust for Data Engineering specialization: Zig from Zero (Zig for Rust developers).
| Path | What it shows | Rust equivalent |
|---|---|---|
hello/ |
First build with zig build-exe, std.io.getStdOut, format strings |
cargo new --bin + println! |
marcopolo/ |
CLI args via argsAlloc, GeneralPurposeAllocator vs page allocator, defer for cleanup |
std::env::args + Drop |
http-server/ |
std.net.Server TCP loop, hand-rolled HTTP response, multi-stage Docker → FROM scratch |
axum / actix-web + cargo build --release |
sqlite-ws/ |
Linking sqlite3 from build.zig, raw C-API queries, WebSocket server |
rusqlite + tokio-tungstenite |
lambda/ |
Custom AL2023 runtime, cross-compile to x86_64-linux-musl, cold-start budget |
cargo lambda + aws-lambda-rust-runtime |
Requires Zig 0.13.0 or newer. The official tarball
or zigup both work; package
managers tend to lag.
git clone https://github.com/paiml/zig-from-zero
cd zig-from-zero
zig version # 0.13.0 or newerEach demo builds with zig build-exe directly — no build.zig
boilerplate needed for the small examples.
# Hello — minimal binary
cd hello && zig build-exe hello.zig && ./hello
# Marco Polo — CLI + arena allocator
cd marcopolo && make && ./marcopolo --name marco
# HTTP server — listens on :8080
cd http-server && make && ./server # or: make docker-run
# Three build modes side by side (~800K → ~100K → ~50K)
cd marcopolo && make dev && make small && make tiny && make sizeZig ships four optimization modes. The course covers all four; the table below is the rule of thumb for picking among them.
| Mode | Flag | When to use |
|---|---|---|
| Debug | (default) | Iteration; full safety + symbol info |
| ReleaseSafe | -O ReleaseSafe |
Production where safety beats raw speed |
| ReleaseFast | -O ReleaseFast |
Throughput-bound workloads |
| ReleaseSmall | -O ReleaseSmall |
Containers, Lambda, embedded |
-fstrip removes symbols; -fsingle-threaded drops thread-safety
guarantees for an extra few KB.
Every push runs the full gate via GitHub Actions:
make fmt-check # zig fmt --check across the tree
make lint # zig ast-check on every .zig source
make test # zig test on hello, marcopolo, http-server
make coverage # kcov per module — fails if any module < 100%
zig build # standard Zig build via build.zig
zig build test # all test blocks via build.zig
zig build run-hello # run a demo from the build graphThe coverage gate is 100% per module. The main body in
marcopolo and http-server is intentionally a thin trampoline around
a tested run / handle function; Zig's test runner drops the
unreferenced main from the test binary, so the kcov number is honest
rather than helped along by exclusion markers.
- Allocators are explicit, lifetimes are implicit. Every function
that allocates takes an
Allocatorparameter; you pick GPA, Arena, page, or fixed-buffer. There is no borrow checker because there is no hidden ownership. - Errors are values, but unioned with the success type.
!TisT \| AnyError;tryis the?operator. Error sets compose, so callers can be exhaustive withoutmatcharms. comptimereplaces traits, generics, and macros. A type is just acomptimevalue. Generic functions takecomptime T: type. Conditional compilation,serde-style codegen, and string-format type-checking all collapse into one feature.deferanderrdeferreplaceDrop. Cleanup is lexical, not type-tied;errdeferruns only on the error path.
The course works through each of these comparisons demo by demo.
MIT. See LICENSE.