Toolchain & Environment

Everything you need is one small binary: the Zig compiler, standard library, and build system ship together and cross-compile out of the box. This lesson installs Zig, runs a first program, and explains the four build modes that drive every decision about speed versus safety.

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).

Thinking in Zig: 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

CommandWhat it doesTypical use
zig run file.zigCompile and execute one filePrototyping and lessons
zig test file.zigCompile and run test blocksUnit tests inside any file
zig buildRun the project's build.zigReal projects with artifacts
zig initScaffold a new projectStarting a structured project
zig fmt file.zigFormat code canonicallyKeeping 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.