Jesse A. Green / notebook

Zig User Guide

Generated June 2026 from ziglang/zig at 738d2be

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software. A single zig executable is simultaneously a compiler for the Zig language, a drop-in C/C++ compiler, a cross-compilation toolchain, a build system, a package manager, a code formatter, and a test runner. This guide shows you how to install Zig and use every part of that toolchain from the command line.

Note

This guide documents Zig 0.16.0 (development series). Zig is pre-1.0 software: the language and standard library still change between releases. Commands and flags described here are taken directly from the compiler source at the documented commit. Always cross-check against zig <command> --help for your installed version.


Table of Contents

  1. Quick Start
  2. Installation
  3. The zig Command
  4. Compiling and Running Single Files
  5. Optimization Modes
  6. Building Projects with zig build
  7. The Package Manager
  8. Cross-Compilation
  9. Using Zig as a C/C++ Compiler
  10. Testing
  11. Formatting Code
  12. Translating C to Zig
  13. The Standard Library
  14. Inspecting Your Environment
  15. Utility and Drop-in Commands
  16. Common Tasks (Recipes)
  17. Troubleshooting
  18. FAQ
  19. Command & Flag Reference
  20. Learning the Zig Language

1. Quick Start

Once Zig is on your PATH, you can write and run a program in under a minute.

Create a file called hello.zig:

const std = @import("std");

pub fn main() void {
    std.debug.print("Hello, {s}!\n", .{"world"});
}

Run it directly — Zig compiles to a native executable and runs it immediately:

zig run hello.zig
Hello, world!

To produce a standalone, optimized binary instead of running it:

zig build-exe hello.zig -O ReleaseFast
./hello
Hello, world!

To start a real, multi-file project with a build script and tests:

mkdir myproject && cd myproject
zig init
zig build run

Tip

Five commands cover almost everything you will do day-to-day: zig run (compile + execute), zig build-exe (produce a binary), zig build (run your project's build script), zig test (run unit tests), and zig fmt (format source). Everything else is a refinement of these.


2. Installation

Zig ships as a single self-contained executable with a lib/ directory beside it. There is no system-wide installation step, no runtime to install, and no dependency on a pre-existing C compiler — Zig bundles its own.

2.1 Download a prebuilt release

Official prebuilt tarballs are distributed from the Zig project's download page. Each archive contains the zig binary and the lib/ directory that holds the standard library and the bundled libc sources.

Linux / macOS

# Extract the archive you downloaded for your platform
tar xf zig-<platform>-<version>.tar.xz
# Move it somewhere on your PATH
sudo mv zig-<platform>-<version> /opt/zig
export PATH="/opt/zig:$PATH"

Windows

# Extract the .zip archive, then add the folder to your PATH
Expand-Archive zig-windows-x86_64-<version>.zip -DestinationPath C:\zig
setx PATH "%PATH%;C:\zig"

Important

The zig executable must stay next to its lib/ directory. If you move the binary on its own, Zig will not find the standard library. If you must relocate the binary alone, point it back at the library with --zig-lib-dir [path] or the ZIG_LIB_DIR environment variable.

2.2 Verify the installation

zig version
0.16.0-dev.xxxx+xxxxxxxxx
zig env
{
 "zig_exe": "/opt/zig/zig",
 "lib_dir": "/opt/zig/lib",
 "std_dir": "/opt/zig/lib/std",
 "global_cache_dir": "/home/you/.cache/zig",
 "version": "0.16.0-dev.xxxx+xxxxxxxxx",
 "target": "x86_64-linux.6.1...6.1-gnu.2.31",
 "env": { ... }
}

If both commands print sensible output, Zig is installed correctly.

Note

Zig has no external runtime dependencies. A Zig binary you build does not require Zig to be installed on the target machine. By default it does not even link a C library — programs are statically self-contained unless you explicitly opt into linking libc with -lc.

2.3 System requirements

Requirement Detail
Disk A few hundred MB for the toolchain (it bundles libc sources for musl, glibc, and wasi, plus libc++ and compiler-rt)
C compiler Not required — Zig is its own C/C++ compiler
LLVM Not required for the default Zig backend; required only for -fllvm, LTO, and C/C++ compilation features marked (requires LLVM extensions)
Network Only for zig fetch / zig build when a project declares package dependencies

3. The zig Command

Run zig with no arguments (or zig help) to see the full command list:

zig help
Usage: zig [command] [options]

Commands:

  build            Build project from build.zig
  fetch            Copy a package into global cache and print its hash
  init             Initialize a Zig package in the current directory

  build-exe        Create executable from source or object files
  build-lib        Create library from source or object files
  build-obj        Create object from source or object files
  test             Perform unit testing
  test-obj         Create object for unit testing
  run              Create executable and run immediately

  ast-check        Look for simple compile errors in any set of files
  fmt              Reformat Zig source into canonical form
  reduce           Minimize a bug report
  translate-c      Convert C code to Zig code

  ar               Use Zig as a drop-in archiver
  cc               Use Zig as a drop-in C compiler
  c++              Use Zig as a drop-in C++ compiler
  dlltool          Use Zig as a drop-in dlltool.exe
  lib              Use Zig as a drop-in lib.exe
  ranlib           Use Zig as a drop-in ranlib
  objcopy          Use Zig as a drop-in objcopy
  rc               Use Zig as a drop-in rc.exe

  env              Print lib path, std path, cache directory, and version
  help             Print this help and exit
  std              View standard library documentation in a browser
  libc             Display native libc paths file or validate one
  targets          List available compilation targets
  version          Print version number and exit
  zen              Print Zen of Zig and exit

The commands fall into a few groups:

Group Commands What they do
Project workflow init, build, fetch Scaffold a package, run its build script, add dependencies
Direct compilation build-exe, build-lib, build-obj, run, test, test-obj Compile source/object files without a build script
Source tooling fmt, ast-check, translate-c, reduce Format, syntax-check, convert C, minimize bug reports
Drop-in C toolchain cc, c++, ar, ranlib, objcopy, rc, dlltool, lib Replace gcc/clang/ar/etc.
Information env, version, targets, libc, std, zen, help Inspect the toolchain

Tip

Every command accepts -h / --help for command-specific usage. The build/run/compile commands share a single large option set — see Section 19.


4. Compiling and Running Single Files

For one-off programs and experiments you do not need a build script. Five commands compile source (and object) files directly.

Command Result
zig run [files] [-- args] Compile to a temporary executable and run it immediately, forwarding args
zig build-exe [files] Create an executable
zig build-lib [files] Create a static or dynamic library
zig build-obj [files] Create a single object file
zig test [files] Compile and run the file's unit tests

4.1 zig run

zig run hello.zig

Pass arguments to your program after --:

zig run main.zig -- --name world --count 3
Hello world (x3)

4.2 zig build-exe

zig build-exe hello.zig
./hello

Name the output and choose an optimization mode:

zig build-exe hello.zig --name greeter -O ReleaseSafe
./greeter

4.3 Supported input file types

build-exe/build-lib/build-obj/run/test accept far more than .zig. You can mix Zig, C, C++, assembly, and precompiled objects in a single command:

Extension File type
.zig Zig source code
.c C source code (requires LLVM extensions)
.cpp .cxx .cc .C .c++ C++ source code (requires LLVM extensions)
.m / .mm Objective-C / Objective-C++ (requires LLVM extensions)
.s Target-specific assembly
.S Assembly with C preprocessor (requires LLVM extensions)
.o ELF / Mach-O / WebAssembly object file
.obj COFF (Windows) object file
.a ELF / Mach-O / WebAssembly static library
.lib COFF (Windows) static library
.so / .dll / .dylib Shared object / DLL / dylib
.tbd macOS text-based dylib definition
.bc LLVM IR module (requires LLVM extensions)

Note

Features marked (requires LLVM extensions) are available only in Zig builds that include LLVM. The standalone Zig backend compiles Zig source without LLVM, but compiling C/C++ and consuming LLVM bitcode needs the LLVM-enabled toolchain (the default for official releases).

4.4 Controlling output artifacts

The -femit-* family controls what the compiler writes. Each has a -fno-emit-* counterpart, and most accept an optional =path:

# Emit assembly instead of a binary
zig build-obj math.zig -femit-asm=math.s -fno-emit-bin

# Emit a C header alongside the object
zig build-lib lib.zig -femit-h
Flag Effect
-femit-bin[=path] (default) Output machine code
-femit-asm[=path] Output .s assembly
-femit-llvm-ir[=path] Output optimized LLVM IR .ll (requires LLVM)
-femit-llvm-bc[=path] Output optimized LLVM bitcode .bc (requires LLVM)
-femit-h[=path] Generate a C header .h
-femit-docs[=path] Generate an HTML docs/ directory
-femit-implib[=path] (default for Windows DLLs) Produce an import .lib

5. Optimization Modes

Zig has four build modes, selected with -O. They trade safety, speed, and binary size. There is no separate "debug vs release" compiler — the mode is a per-build choice.

Mode Optimizations Safety checks Use for
Debug Off (default) On Development; fast compiles, runtime safety
ReleaseSafe On On Production where safety matters more than max speed
ReleaseFast On Off Maximum runtime performance
ReleaseSmall On Off Smallest binary size
zig build-exe app.zig -O ReleaseFast

Important

In Debug and ReleaseSafe, Zig inserts runtime safety checks (integer overflow, out-of-bounds access, invalid casts, unreachable, etc.) that turn undefined behavior into a defined panic. ReleaseFast and ReleaseSmall remove these checks for speed/size — bugs that were caught as panics become genuine undefined behavior. Prefer ReleaseSafe unless profiling proves you need ReleaseFast.

Tip

Optimization mode is a per-module setting. With the -M module syntax (or in a build script) you can compile your own code in ReleaseSafe while a hot dependency uses ReleaseFast. When using zig build, request a mode with --release[=fast|safe|small] or expose it through b.standardOptimizeOption().


6. Building Projects with zig build

For anything larger than a single file, Zig projects use a build script written in Zig: build.zig. Running zig build compiles and executes that script, which constructs a graph of build steps that the build runner then executes in parallel with automatic caching.

6.1 Scaffold a project

zig init
info: created build.zig
info: created build.zig.zon
info: created src/main.zig
info: created src/root.zig

zig init creates a runnable, testable project:

File Purpose
build.zig The build script (defines steps: build, run, test)
build.zig.zon The package manifest (name, version, dependencies)
src/main.zig Executable entry point (pub fn main)
src/root.zig Library root module (re-exported public API)

Use -m / --minimal for a bare template with fewer comments:

zig init --minimal

6.2 Anatomy of build.zig

A build.zig exposes one function, pub fn build(b: *std.Build) void, which describes the build rather than performing it. The functions in std.Build form a DSL for declaring modules, artifacts, and the dependencies between steps:

const std = @import("std");

pub fn build(b: *std.Build) void {
    // Let the user pick target and optimization on the command line.
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // A reusable module (importable by consumers of this package).
    const mod = b.addModule("mymod", .{
        .root_source_file = b.path("src/root.zig"),
        .target = target,
    });

    // An executable whose root module imports the library module.
    const exe = b.addExecutable(.{
        .name = "myproject",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
            .imports = &.{ .{ .name = "mymod", .module = mod } },
        }),
    });

    // Install the binary into zig-out/ when `zig build` runs.
    b.installArtifact(exe);

    // Wire up `zig build run`.
    const run_step = b.step("run", "Run the app");
    const run_cmd = b.addRunArtifact(exe);
    run_step.dependOn(&run_cmd.step);
}

Note

The build script is declarative: it mutates the build graph b, which an external runner then executes. Because the graph encodes dependencies between steps, the runner parallelizes the build automatically and the cache system knows exactly when a step can be skipped.

6.3 Running build steps

zig build              # Run the default install step (produces zig-out/)
zig build run          # Run a step named "run"
zig build test         # Run a step named "test"
zig build --list-steps # See every step this project defines
zig build --help       # Steps + project-specific options

The default output directory is zig-out/. Override it with --prefix:

Flag Effect
-p, --prefix [path] Install location (default zig-out)
--prefix-lib-dir [path] Where libraries are installed
--prefix-exe-dir [path] Where executables are installed
--prefix-include-dir [path] Where C headers are installed

6.4 Choosing release mode and target

zig build exposes the same target and optimization choices as direct compilation:

# Release build for the host
zig build --release=fast

# Cross-compile (when build.zig uses standardTargetOptions)
zig build -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSmall

--release[=mode] requests release mode (fast, safe, or small). Project-specific -D options are declared by the build script via b.option() and appear in zig build --help under Project-Specific Options.

6.5 Iterating: watch, summary, and parallelism

Flag Effect
--watch Continuously rebuild when source files change
--debounce <ms> Delay before rebuilding after a change is detected
-j<N> Limit concurrent jobs (default: all CPU cores)
--maxrss <bytes> Limit memory usage
--summary [mode] Build summary: all, new, failures, line, none
--prominent-compile-errors (via --error-style) Control error verbosity
-l, --list-steps List available steps and exit
--verbose Print each command before executing it
# Rebuild and re-run tests on every save
zig build test --watch

Tip

zig build has built-in fuzzing and a web UI. zig build --fuzz continuously searches your unit tests for failures (implies --webui); zig build --time-report forces a full rebuild and reports per-file compilation timings in the browser. Use --webui[=ip] to expose the interface on a specific address.

6.6 System integration

When a project links system libraries, the build runner can be told to use the host's installed libraries instead of fetched/built ones:

Flag Effect
--search-prefix [path] Add a path to look for binaries, libraries, headers
--sysroot [path] Set the system root directory
--libc [file] Provide a libc paths file
--system [pkgdir] Disable package fetching; enable all system integrations
-fsys=[name] / -fno-sys=[name] Enable/disable a specific system integration

7. The Package Manager

Zig's package manager is built into the toolchain. Dependencies are declared in build.zig.zon (a Zig Object Notation file) and consumed in build.zig.

7.1 The build.zig.zon manifest

.{
    .name = .myproject,
    .version = "0.1.0",
    .fingerprint = 0x1234567890abcdef, // auto-generated, never edit
    .minimum_zig_version = "0.16.0",
    .dependencies = .{
        .some_pkg = .{
            .url = "git+https://example.com/user/some_pkg.git#<commit>",
            .hash = "...",
        },
    },
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}
Field Required Meaning
name Yes Enum literal, valid bare Zig identifier, ≤32 bytes. Default key consumers use to import this package.
version Yes semver string, ≤32 bytes
fingerprint Yes 64-bit globally-unique package id, auto-generated and never changed. Regenerate (delete + zig build) only when forking a still-maintained project.
minimum_zig_version No Advisory minimum Zig version (semver)
dependencies No Table of url + hash (or path) entries
paths No Files/directories included in the package's content hash

Caution

Do not hand-edit the fingerprint. It is how Zig detects that two packages are versions of the same project. Copying another project's fingerprint into your fork is, in the toolchain's own words, a hostile act that attempts to hijack the original's identity. Regenerate it (delete the field, run zig build) when forking.

7.2 Adding a dependency with zig fetch

zig fetch downloads a package into the global cache, computes its content hash, and (with --save) writes the entry into build.zig.zon for you:

zig fetch --save git+https://github.com/user/fun-tool.git
zig fetch --save https://example.com/user/tool/archive/refs/heads/master.tar.gz

A <url> may point to a git+http/git+https server, a (optionally compressed) tarball, or a git bundle file. A <path> fetches from a local directory.

Flag Effect
--save Add the fetched package to build.zig.zon
--save=[name] Save under a specific dependency name
--save-exact Save and store the URL verbatim
--global-cache-dir [path] Override the global cache directory
--debug-hash Print verbose hash information

In build.zig, pull the dependency into your build graph:

const dep = b.dependency("fun_tool", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("fun_tool", dep.module("fun_tool"));

7.3 Fetching the whole dependency tree

zig build --fetch          # Fetch needed (lazy) dependencies and exit
zig build --fetch=all      # Fetch every dependency, even lazy ones

Note

Dependencies are content-addressed by hash and stored in the global cache (see zig env's global_cache_dir), shared across all your projects. A given package version is downloaded once and reused everywhere.


8. Cross-Compilation

Cross-compilation is Zig's signature feature. Because the toolchain bundles the libc sources for every supported target, you can compile for any supported OS/architecture from any host — no cross-toolchain, no sysroot, no extra downloads.

8.1 The -target triple

A target is written <arch><sub>-<os>-<abi>:

# From an x86-64 Linux host, build a Windows ARM64 executable:
zig build-exe hello.zig -target aarch64-windows-gnu

# A statically-linked Linux musl binary:
zig build-exe hello.zig -target x86_64-linux-musl -O ReleaseSmall

# A 64-bit RISC-V Linux binary:
zig build-exe hello.zig -target riscv64-linux-musl

List everything the compiler can target:

zig targets

This prints the available architectures, operating systems, ABIs, CPUs, and CPU feature sets as JSON.

8.2 Selecting a specific CPU

# Target a baseline CPU for maximum portability
zig build-exe app.zig -target x86_64-linux -mcpu=baseline

# Target a specific microarchitecture
zig build-exe app.zig -target x86_64-linux -mcpu=znver3

# Native CPU with all detected features (the default for native builds)
zig build-exe app.zig -mcpu=native

8.3 Glibc version selection

When targeting GNU/Linux you can pin a specific glibc version directly in the triple, and Zig builds the matching stub libraries on demand:

# Link against glibc 2.19 or later
zig build-exe app.zig -target x86_64-linux-gnu.2.19

Important

Zig supports glibc versions back to 2.17 (2012) for Zig programs that use the standard library, and back to 2.2.5 when used purely as a C compiler (zig cc). The glibc "libraries" Zig generates are stub libraries used only for compile-time linking — the real glibc on the target machine provides the implementations at run time.

8.4 Object format override

zig build-obj app.zig -ofmt=wasm     # WebAssembly
zig build-obj app.zig -ofmt=c        # Transpile to C source

Supported -ofmt values: elf, c, wasm, coff, macho, spirv, plan9 (hex and raw are planned).

Tip

-ofmt=c makes Zig emit portable C source instead of machine code — useful for bootstrapping onto platforms without a native Zig backend, or for embedding Zig logic into a C build.


9. Using Zig as a C/C++ Compiler

zig cc and zig c++ are drop-in replacements for gcc/clang and g++/clang++. They accept Clang-compatible flags and inherit Zig's bundled headers and cross-compilation.

# Compile a C program exactly as you would with cc
zig cc -o hello hello.c

# Cross-compile C to Windows from Linux:
zig cc -o hello.exe hello.c -target x86_64-windows-gnu

# Use it as your project's CC:
CC="zig cc" CXX="zig c++" ./configure
make

Because Zig bundles musl, glibc stubs, wasi-libc, libc++, libc++abi, libunwind, and compiler-rt, zig cc can cross-compile C and C++ projects to dozens of targets with no additional toolchain. This is why many non-Zig projects adopt zig cc purely as a portable cross-compiler.

Note

zig cc is a thin wrapper over the bundled Clang frontend; it understands GCC/Clang command-line conventions including -I, -L, -l, -D, -O2, -std=, -Wall, response files, and so on. Mixing C and Zig in one build-exe/build-lib invocation works because the same machinery backs both.

9.1 The other drop-in tools

Zig also stands in for the rest of a typical C toolchain, so a build system can call a single zig for everything:

Command Replaces
zig cc cc / clang
zig c++ c++ / clang++
zig ar ar (archiver)
zig ranlib ranlib
zig lib lib.exe (MSVC librarian)
zig objcopy objcopy
zig rc rc.exe (Windows resource compiler)
zig dlltool dlltool

10. Testing

Zig has a built-in test runner. Tests are declared inline with the test keyword and run with zig test or a test step in zig build.

const std = @import("std");
const expect = std.testing.expect;

fn add(a: i32, b: i32) i32 {
    return a + b;
}

test "add works" {
    try expect(add(2, 3) == 5);
}
zig test mymodule.zig
1/1 mymodule.test.add works... OK
All 1 tests passed.

10.1 Test options

Flag Effect
--test-filter [text] Run only tests whose name contains text
--test-cmd [arg] Run the test binary under a wrapper command (one arg at a time)
--test-cmd-bin Append the test binary path to the wrapper command
--test-no-exec Compile the test binary without running it
--test-runner [path] Use a custom test runner
# Run only tests mentioning "parser"
zig test src/parser.zig --test-filter parser

# Cross-compile tests and run them under QEMU
zig test app.zig -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin

Tip

When running tests through zig build, add --fuzz to continuously search for failing inputs, and --test-timeout <dur> (e.g. --test-timeout 30s) to terminate tests that hang. The test runner reports memory leaks automatically when you use std.testing.allocator.


11. Formatting Code

zig fmt rewrites Zig source into the language's single canonical style — there are no formatting options to bikeshed.

# Format files and directories in place (recursive)
zig fmt src/

# Check formatting in CI without modifying files
zig fmt --check src/
src/main.zig
src/parser.zig
Flag Effect
--check List non-conforming files and exit non-zero if any (does not modify)
--stdin Read from stdin, write formatted output to stdout
--ast-check Run zig ast-check on every file while formatting
--exclude [file] Exclude a file or directory
--zon Treat all inputs as ZON regardless of extension
--color [auto|off|on] Colored error messages

Important

zig fmt (without --check) modifies files in place. Run it on a clean working tree or use --check in automated pipelines.

Tip

zig ast-check [file] performs a fast syntax/semantic sanity check without producing any output binary — ideal for editor integrations and pre-commit hooks.


12. Translating C to Zig

zig translate-c converts a C source file into equivalent Zig code, printed to stdout. It is how @cImport works under the hood and is useful for porting C to Zig or for inspecting how a C API maps into the language.

zig translate-c -lc header_demo.c > demo.zig

translate-c accepts the same target and include-path flags as the compile commands (-target, -I, -D, -isystem, etc.), so you can translate a header as it would appear for a specific platform.

Note

translate-c requires the LLVM/Clang-enabled toolchain (it parses C through Clang). The output is real, compilable Zig — read it, edit it, and commit it, or @cImport the header directly and let Zig translate it on the fly.


13. The Standard Library

The standard library lives under lib/std/ and is imported as @import("std"). It is large and batteries-included. The most-used namespaces:

Namespace Contents
std.mem Memory utilities: Allocator, copying, comparison, slicing, tokenizing
std.heap Allocators: GeneralPurposeAllocator, ArenaAllocator, page_allocator, FixedBufferAllocator
std.fmt Formatting: std.fmt.allocPrint, format strings, parsing numbers
std.fs Filesystem: files, directories, paths, walking
std.io / std.Io Readers, writers, buffered streams
std.process Args, environment variables, child processes, exit
std.ArrayList Growable array
std.AutoHashMap / std.StringHashMap Hash maps
std.json JSON parse/stringify
std.crypto Hashes, AEAD ciphers, signatures, KDFs
std.http HTTP client and server
std.Thread Threads, mutexes, condition variables
std.testing Test assertions and the testing allocator
std.math Math functions and constants
std.Build The build-system DSL used in build.zig
std.Target Target description (arch/os/abi/cpu)
std.debug print, stack traces, assertions
std.sort Sorting algorithms
std.unicode UTF-8 / UTF-16 handling

Other namespaces include atomic, base64, compress, dwarf, elf, macho, coff, pdb, tar, zip, time, Uri, SemanticVersion, Random, simd, enums, meta, ascii, and the binary-format helpers.

13.1 Browse the standard library docs locally

zig std

This starts a local web server and opens the standard library's generated API documentation in your browser, exactly matching your installed version.

Tip

Allocators are explicit in Zig — most standard-library containers and functions take an Allocator parameter. For application code, std.heap.GeneralPurposeAllocator (debug builds, leak detection) and std.heap.ArenaAllocator (bulk-free) are the common choices; std.heap.page_allocator is a simple fallback.


14. Inspecting Your Environment

zig version        # Just the version string
zig env            # Full environment as JSON
zig targets        # All supported targets/CPUs/features as JSON
zig zen            # The Zen of Zig

zig env reports the values Zig resolved for this invocation:

Field Meaning
zig_exe Path to the running zig binary
lib_dir Zig installation lib/ directory
std_dir Standard library directory
global_cache_dir Where fetched packages and global build artifacts are cached
version Compiler version
target The native target triple
env Resolved environment variables affecting Zig

The Zen of Zig captures the language's design philosophy:

 * Communicate intent precisely.
 * Edge cases matter.
 * Favor reading code over writing code.
 * Only one obvious way to do things.
 * Runtime crashes are better than bugs.
 * Compile errors are better than runtime crashes.
 * Incremental improvements.
 * Avoid local maximums.
 * Reduce the amount one must remember.
 * Focus on code rather than style.
 * Resource allocation may fail; resource deallocation must succeed.
 * Memory is a resource.
 * Together we serve the users.

15. Utility and Drop-in Commands

15.1 zig libc

Detect or validate a libc installation paths file, used for native compilation against a system libc:

# Print the detected native libc paths (save and edit for cross-compiling)
zig libc > my-libc.txt

# Validate a paths file you wrote
zig libc my-libc.txt

# Print the include directories for a target
zig libc -target x86_64-linux-gnu -includes

Pass the resulting file to any compile command with --libc my-libc.txt.

15.2 zig objcopy

zig objcopy [options] input output

A drop-in objcopy for stripping, format conversion, and section manipulation of object files and binaries.

15.3 zig reduce

zig reduce minimizes a Zig source file that triggers a compiler bug, repeatedly shrinking it while a check still reproduces the failure — invaluable when filing a bug report against the compiler.

15.4 zig ast-check

zig ast-check src/main.zig

Fast syntax and early-semantic checking with no output artifact. Used by editors and as a lightweight pre-commit gate.


16. Common Tasks (Recipes)

Run a script-style program quickly

zig run tool.zig -- arg1 arg2

Produce a small, statically-linked Linux binary

zig build-exe app.zig -target x86_64-linux-musl -O ReleaseSmall -fstrip

Build a shared library with a C header

zig build-lib mylib.zig -dynamic -femit-h --name mylib

Cross-compile a C project with Zig as the compiler

CC="zig cc -target aarch64-linux-musl" make

Continuously test on save

zig build test --watch

Add and use a dependency

zig fetch --save git+https://github.com/user/pkg.git
# then in build.zig: b.dependency("pkg", .{...})
zig build

Strip debug info from an existing binary

zig objcopy --strip-all app app-stripped

Emit assembly to inspect codegen

zig build-obj hot.zig -O ReleaseFast -femit-asm=hot.s -fno-emit-bin

17. Troubleshooting

Error: unable to find zig installation directory

Cause — The zig binary was moved away from its lib/ directory, so it cannot locate the standard library. Solution — Keep zig next to lib/, or pass --zig-lib-dir /path/to/lib (or set ZIG_LIB_DIR). Prevention — Install the toolchain as a unit; symlink the binary onto your PATH instead of copying it.

Error: error: unable to parse command line parameters: NestedResponseFile

Cause — zig cc was given a response file (@file) that itself references another @file. Nested @ response files are not currently supported (see issue #24386). Solution — Flatten the response files into a single non-nested @args file, or pass the arguments directly. Prevention — Avoid build systems that emit nested response files when using zig cc.

Error: 'Io.File' has no member named 'WriteError'

Cause — Code written for an older standard-library API. The std.Io / std.fs interfaces change across pre-1.0 releases (see issue #25857). Solution — Update the code to the current API, or pin the Zig version your code was written for. Check zig std for the exact signatures of your installed version. Prevention — Set minimum_zig_version in build.zig.zon and test against the Zig version you target.

Error: a C/C++ feature reports requires LLVM extensions

Cause — You invoked a feature (compiling .c/.cpp, -flto, -femit-llvm-ir) on a Zig build that does not include LLVM. Solution — Use an official release (LLVM-enabled) or a toolchain built with LLVM. Prevention — Reserve the standalone (no-LLVM) backend for pure-Zig compilation.

Error: linker cannot find a system library (-lfoo)

Cause — The library is not on the search path, or you are cross-compiling and the target library is unavailable. Solution — Add -L/path/to/libs, supply a libc paths file with --libc, or use --search-prefix / --sysroot under zig build. Prevention — For portable builds, prefer statically linking or targeting musl so no system libraries are needed.

Build is slow or uses too much memory

Cause — Default parallelism uses all cores/memory; large comptime work can be heavy. Solution — Limit with -j<N> and --maxrss <bytes>; use --skip-oom-steps to skip steps that would exceed the memory limit; enable -fincremental for faster rebuilds. Prevention — Keep comptime evaluation bounded; use --watch so only changed steps rebuild.

zig build cannot fetch a dependency

Cause — Network access is blocked, or the dependency hash/URL in build.zig.zon is wrong. Solution — Run zig build --fetch to fetch explicitly; re-run zig fetch --save <url> to refresh the hash; check that the URL points to a git server, tarball, or git bundle. Prevention — Commit build.zig.zon with pinned hashes; pre-fetch with --fetch=all in CI.

A program crashes only in ReleaseFast

Cause — ReleaseFast/ReleaseSmall disable the runtime safety checks that turned undefined behavior into panics in Debug/ReleaseSafe. Solution — Reproduce in ReleaseSafe (-O ReleaseSafe) to get a defined panic and a stack trace, then fix the underlying undefined behavior. Prevention — Test in ReleaseSafe; only switch to ReleaseFast for code paths proven correct and profiled as hot.


18. FAQ

Q: Do I need to install LLVM, GCC, or Clang to use Zig? No. The zig executable is a complete toolchain. It compiles Zig without LLVM (default backend) and includes its own bundled Clang for C/C++ work. You do not need a pre-existing compiler.

Q: Do programs I build require Zig (or a runtime) to be installed on the target? No. Zig produces standalone native binaries with no runtime. By default they do not even link libc — they are statically self-contained unless you opt into -lc or dynamic linking.

Q: How is cross-compilation "free"? Zig bundles the libc sources (musl, glibc stubs, wasi-libc) plus libc++, compiler-rt, and libunwind. It builds exactly what a given target needs on demand, so any host can target any supported platform with no extra toolchain or sysroot.

Q: What is the difference between zig run and zig build run? zig run file.zig compiles and runs a single file directly. zig build run executes the run step defined in your project's build.zig (which can involve many modules, dependencies, and custom steps).

Q: Why is there no config file for zig fmt? By design. Zig has exactly one canonical style; zig fmt enforces it with no options, eliminating formatting debates.

Q: What does -O ReleaseSafe give me that ReleaseFast does not? Runtime safety checks (overflow, bounds, invalid casts, unreachable) are kept on in ReleaseSafe while optimizations are enabled. ReleaseFast removes those checks for maximum speed.

Q: How do I see every flag for a command? Run zig <command> --help. The build/compile commands share one large option set; zig build --help additionally lists your project's -D options.

Q: How do I pick which CPU to optimize for? Use -mcpu. -mcpu=native (default for native builds) uses your host CPU's features; -mcpu=baseline maximizes portability; -mcpu=<name> targets a specific microarchitecture. zig targets lists valid CPU names.

Q: Is Zig stable / production-ready? Zig is pre-1.0. The language and standard library still change between releases. Many teams ship Zig (and zig cc) in production, but you should pin a version and expect to update code across releases.

Q: Where do I report a compiler bug? Minimize it with zig reduce first, then file an issue. A small reproducer dramatically speeds up triage.

Q: Can I compile to WebAssembly? Yes — -target wasm32-wasi (or wasm32-freestanding) and/or -ofmt=wasm. The WebAssembly-specific link options (--import-memory, --export-memory, --initial-memory, --max-memory, --shared-memory, --export=) are available on the compile commands.

Q: How do I run my unit tests on a different architecture? Cross-compile the test binary and run it under an emulator: zig test app.zig -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin. Under zig build, the runner integrates with QEMU/Wine/Rosetta/wasmtime via -fqemu, -fwine, -frosetta, -fwasmtime.


19. Command & Flag Reference

19.1 General options (compile/run/test commands)

Flag Effect
-h, --help Print command-specific help
--color [auto|off|on] Colored error messages
-j<N> Limit concurrent jobs
-fincremental / -fno-incremental Enable/disable incremental compilation
--cache-dir [path] Override the local cache directory
--global-cache-dir [path] Override the global cache directory
--zig-lib-dir [path] Override the Zig lib/ directory
--show-builtin Print the source of @import("builtin") and exit

19.2 Global compile options

Flag Effect
--name [name] Output/compilation-unit name
--libc [file] libc paths file
-x language Treat subsequent inputs as language
--dep [[import=]name] Add an entry to the next module's import table
-M[name][=src] Create a module from current per-module settings (first -M is the main module)
--error-limit [num] Max number of distinct error values
-fllvm / -fno-llvm Force/prevent the LLVM codegen backend
-fclang / -fno-clang Force/prevent Clang as the C/C++ backend
-fPIE / -fno-PIE Position-independent executable
-flto / -fno-lto Link-time optimization (requires LLVM)
-freference-trace[=num] / -fno-reference-trace Lines of reference trace per error
-ffunction-sections / -fdata-sections Each function/datum in its own section
-municode (Windows) use wmain/wWinMain entry
--time-report Send timing diagnostics to --listen clients

19.3 Per-module compile options

Flag Effect
-target [name] <arch><sub>-<os>-<abi> target triple
-O [Debug|ReleaseFast|ReleaseSafe|ReleaseSmall] Optimization mode
-ofmt=[elf|c|wasm|coff|macho|spirv|plan9] Object format override
-mcpu [cpu] Target CPU and feature set
-mcmodel=[model] Code model (default, small, kernel, large, medany, …)
-fPIC / -fno-PIC Position-independent code
-fstack-check / -fstack-protector Stack probing / protection
-fsanitize-c[=trap|full] / -fno-sanitize-c C undefined-behavior detection
-fsanitize-thread / -fno-sanitize-thread Thread Sanitizer
-ffuzz / -fno-fuzz Fuzz-testing instrumentation
-funwind-tables / -fno-unwind-tables Unwind table generation
-fsingle-threaded Assume a single thread
-fstrip / -fno-strip Omit/keep debug symbols
-I[dir], -isystem [dir], -idirafter [dir] C include search paths
-D[macro]=[value] Define a C macro
-cflags [flags] -- Extra flags for the next C source files
Flag Effect
-l[lib], --library [lib] Link a system library (if used)
-L[dir], --library-directory [dir] Library search path
-rpath [path] Runtime library search path
-static / -dynamic Force static/dynamic linking
-T[script], --script [script] Custom linker script
--dynamic-linker [path] / --no-dynamic-linker Dynamic interpreter path
-flld / -fno-lld Force/prevent LLD as the linker
--gc-sections / --no-gc-sections Remove unreachable functions/data
--build-id[=style] Embed a build ID (fast, sha1, md5, uuid, none, …)
-z [arg] Linker extension flags (now, relro, defs, notext, …)
--stack [size] Override default stack size
WebAssembly: --import-memory, --export-memory, --initial-memory=, --max-memory=, --shared-memory, --export= WASM linear-memory and export control
Darwin: -framework [name], -F[dir], -install_name=, -dead_strip macOS frameworks and linker behavior

Note

This reference is a curated subset. For the exhaustive, version-exact list run zig build-exe --help; for the build runner run zig build --help.


20. Learning the Zig Language

This guide covers the toolchain — how to drive the zig binary. The Zig language itself (syntax, comptime, error unions, optionals, generics via types-as-values, allocators, async, the full type system) is documented in the project's canonical Language Reference that ships with every Zig release as doc/langref.html.in and is published online by the Zig project.

The language reference is the authoritative, exhaustive source for language semantics. Its major sections include:

  • Fundamentals — Hello World, Comments, Values, Primitive Types, Variables, Integers, Floats, Operators
  • Aggregate types — Arrays, Vectors (SIMD), Pointers, Slices, struct (incl. extern/packed), enum, union/Tagged unions, opaque, Tuples
  • Control flow — Blocks, switch (incl. labeled/exhaustive), while, for, if, defer/errdefer
  • Functions and errors — Functions, Errors and error unions, Optionals
  • Compile-time — comptime, generic data structures, @import, builtins
  • Testing — Test declarations, doctests, the testing namespace
  • C interop — @cImport, extern, calling conventions, translate-c
  • Memory — Allocators, alignment, sentinel-terminated pointers/arrays/slices

Tip

To read the language reference offline at exactly your installed version, the doc/langref.html.in file lives inside your Zig installation. For API-level standard-library documentation, run zig std to browse it in your browser.