Toolchain & Environment
Install Zig
Zig publishes a single tarball/zip per platform from ziglang.org/download. There is no dependency to install first: the tarball contains the compiler and its standard library, and it works from any folder.
Download and Verify
Pick the archive for your operating system (for example zig-x86_64-windows.zip on 64-bit Windows), extract it anywhere, and place the folder on your PATH. Package managers also work where you prefer them:
# Linux (Snap)
sudo snap install zig --classic --beta
# macOS (Homebrew)
brew install zig
# Windows (winget)
winget install zig.zig
After installation, open a terminal and confirm the toolchain responds. Zig is careful about backward compatibility, but each release can still adjust details, so know your version:
zig version # e.g. 0.15.2
zig env # compiler paths, target, and library dirs
Add to PATH
On Windows, extracting the zip does not put zig.exe on the command path. Open System Properties → Environment Variables, append the folder that contains zig.exe to Path, then open a fresh terminal. On Linux and macOS the package managers handle this for you.
Verify by typing zig alone — you should see the usage summary with subcommands like build, run, test, and fmt.
First Program
C-family programmers expect an entry point named main. Zig keeps that tradition, with one Zig-specific twist: main can return an error union, so the runtime prints any unhandled failure with a stack trace instead of silently exiting.
Hello, World
std.debug.print is the simplest output function. Its format string works like C's printf, but the arguments travel in a tuple (.{...}) so the compiler checks the count and types for you.
const std = @import("std");
// !void means "void, or an error" — errors bubble up to the runtime.
pub fn main() !void {
// {s} is the placeholder for a string argument.
std.debug.print("Hello, World!\n", .{});
std.debug.print("The year is {d}\n", .{2026});
}
Run It
Zig compiles and runs a single file with one command. Behind the scenes a real binary is produced, cached in .zig-cache, then executed:
zig run hello.zig
# Hello, World!
# The year is 2026
Compare with a C version: Zig has no separate compile and link steps for this case — one tool does both, and the same binary can target a different CPU and OS via flags (cross-compilation is a first-class feature, not an afterthought).
pub fn main() !void is already a lesson. The ! marks an error union: the function either returns nothing or an error. Failure is a typed value in the signature, never an invisible exception. You will decode this type fully in the Errors lesson.
Essential Commands
Five subcommands carry you through every lesson in this track. The fourth, build, uses a build.zig file that you will author in the Build System lesson.
Command Reference
| Command | What it does | Typical use |
|---|---|---|
zig run file.zig | Compile and execute one file | Prototyping and lessons |
zig test file.zig | Compile and run test blocks | Unit tests inside any file |
zig build | Run the project's build.zig | Real projects with artifacts |
zig init | Scaffold a new project | Starting a structured project |
zig fmt file.zig | Format code canonically | Keeping style uniform |
Build Modes
Every Zig compilation runs in one of four modes. Modes are chosen per scope: a whole artifact by default, or a single function or block with @setRuntimeSafety. The mode decides whether the cost of safety checks is paid in the produced machine code.
Debug versus Release
Debug (the default) is unoptimized and maximizes error quality: fastest compilation, full safety, and the clearest stack traces. ReleaseSafe enables optimization while keeping safety — the production default. ReleaseFast and ReleaseSmall trade safety for speed or size. You already saw the full table in the overview lesson; the practical rule is simple: debug while developing, release-safe for anything you ship, and release-fast only on measured hot paths.
Project Layout
Zig projects use an optional but conventional layout. zig init generates it for you; knowing it means you always know where source lives and where artifacts go.
zig init Layout
my-project/
├── build.zig # build recipe — Zig code, not a DSL
├── build.zig.zon # package metadata (name, version, dependencies)
├── src/ # application source
│ ├── main.zig
│ └── root.zig # optional library root module
└── .zig-cache/ # generated — never commit, never edit
The build script is written in Zig itself and executed by the compiler — that single fact removes an entire class of build-tool bugs and keeps configurability infinite. build.zig.zon declares dependencies that zig build fetches and pins.
Editor Support
Zig ships its own language server (zig env shows its path) and a canonical formatter. Any editor with LSP support — VS Code with the official extension, Neovim with zls, or JetBrains IDEs — gives you completion, hover docs, and inline errors for free.
Language Server and Formatter
Configure your editor to run zig fmt on save. Canonical formatting is not a preference — it is a community norm, so code from different authors looks consistent, and the compiler can even check it with zig fmt --check in CI.
Next: the syntax lesson — how statements and expressions are written in Zig.