TechSetupGuides
Intermediatezigprogramming-languagesystems-programmingcompilerc-interoperabilitybuild-systemmanual-memory-managementperformancecross-compilationnative-package-manager

Zig - General-Purpose Programming Language

Install and use Zig, a general-purpose programming language and toolchain designed for maintaining robust, optimal, and reusable software — covering installation, language features, build system, C interoperability, and best practices.

  1. Step 1

    Overview

    Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software. It is designed to be simple, provide explicit and predictable behavior, and eliminate hidden control flow. Zig compiles to native code and can interoperate seamlessly with C code.

    Key Features:

    • No Hidden Control Flow: No exceptions, no implicit allocations, no virtual method tables
    • Manual Memory Management: Full control over memory with optional garbage collection
    • C Interoperability: Import C headers directly, link with C libraries
    • Compile-Time Code Execution: Run Zig code at compile-time for code generation
    • No Preprocessor: Everything is in the language itself
    • Undefined Behavior Avoidance: Safe by default, explicit unsafe blocks
    • Native Build System: Built-in build system, no separate tool needed
    • Package Manager: Native package management with dependency resolution
    • Cross-Compilation: Built-in cross-compilation for multiple platforms
    • Performance: Comparable to C/C++ with zero-cost abstractions

    Philosophy:

    • Simplicity: Small language surface, easy to learn and understand
    • Pragmatism: Solves real-world problems without academic purity
    • Optimality: Generates efficient code without hidden costs
    • Maintainability: Easy to refactor, no hidden behavior
    Official Docs: https://ziglang.org/documentation/master/
    Documentation (Unofficial): https://ziglearn.org
    GitHub (Mirror): https://github.com/ziglang/zig (43K+ stars)
    Codeberg (Primary): https://codeberg.org/ziglang/zig (5K+ stars)
    Zig Packages: https://zigls.dev
    Zig Standard Library: https://github.com/ziglang/zig/tree/master/lib/std
  2. Step 2

    Installation

    Zig can be installed via package managers, pre-built binaries, or from source. The self-hosted compiler (Zig 0.11+) is now stable and recommended.

    Installation methods:

    1. Package managers (recommended for most users)
    2. Pre-built binaries from Zig website
    3. Build from source (for latest development version)
    # Option 1: Package Managers (Recommended)
    
    # macOS (Homebrew)
    brew install zig
    
    # Linux (Nix)
    nix-shell -p zig
    
    # Linux (Snap)
    sudo snap install zig --classic
    
    # Windows (Scoop)
    scoop install zig
    
    # Windows (Chocolatey)
    choco install zig
    
    # Option 2: Pre-built Binaries
    # Download from: https://ziglang.org/download/
    # Or use the installer script:
    
    # macOS/Linux
    wget https://ziglang.org/download/0.13.0/zig-linux-x86_64-0.13.0.tar.xz
    tar xf zig-linux-x86_64-0.13.0.tar.xz
    sudo mv zig-linux-x86_64-0.13.0 /usr/local/lib/zig
    sudo ln -s /usr/local/lib/zig/zig /usr/local/bin/zig
    
    # Windows
    # Download ZIP from ziglang.org/download
    # Extract to C:\ zig
    # Add C:\zig to PATH
    
    # Option 3: Build from Source (Requires Zig 0.11+ to build)
    git clone https://github.com/ziglang/zig.git
    cd zig
    make
    sudo make install
    
    # Verify installation
    zig version
    # Should print: zig version X.Y.Z
  3. Step 3

    First Zig Program

    Create and run your first Zig program. Zig has a built-in build system that handles compilation and linking.

    # Create project directory
    mkdir hello_zig
    cd hello_zig
    
    # Create main.zig
    cat > main.zig << 'EOF'
    const std = @import("std");
    
    pub fn main() !void {
        const stdout = std.io.getStdOut().writer();
        try stdout.print("Hello, Zig!\n", .{});
    }
    EOF
    
    # Compile and run
    zig run main.zig
    # Output: Hello, Zig!
    
    # Compile to executable
    zig build-exe main.zig -o hello
    ./hello
    
    # Compile with optimizations
    zig build-exe main.zig -O ReleaseFast -o hello_opt
    
    # Compile for different targets
    zig build-exe main.zig -target x86_64-windows -o hello.exe
    zig build-exe main.zig -target wasm31-freestanding -o hello.wasm
  4. Step 4

    Build System

    Zig has a built-in build system that replaces make, cmake, and other build tools. The build.zig file defines your project structure, dependencies, and build steps.

    # Initialize a new project with build.zig
    zig init
    
    # Creates:
    # my_project/
    # ├── build.zig          # Build configuration
    # ├── build.zig.zon      # Package manifest
    # ├── src/
    # │   └── main.zig       # Main source file
    # └── test/
    #     └── main.zig       # Tests
    
    # Example build.zig:
    const std = @import("std");
    
    pub fn build(b: *std.Build) void {
        // Target platform
        const target = b.standardTargetOptions(.{});
        
        // Optimization level
        const optimize = b.standardOptimizeOption(.{});
        
        // Main library
        const lib = b.addExecutable(.{
            .name = "my_app",
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        });
        
        // Install executable
        b.installArtifact(lib);
        
        // Add test step
        const unit_tests = b.addTest(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        });
        
        const run_unit_tests = b.addRunArtifact(unit_tests);
        
        // Build steps
        const build_step = b.step("build", "Build application");
        build_step.dependOn(&lib.step);
        
        const test_step = b.step("test", "Run unit tests");
        test_step.dependOn(&run_unit_tests.step);
    }
    
    # Build commands
    zig build                  # Build default step
    zig build test             # Run tests
    zig build -Doptimize=ReleaseFast  # Optimized build
    zig build -Dtarget=x86_64-windows  # Cross-compile
  5. Step 5

    Language Basics

    Zig has a C-like syntax with modern features. Variables are immutable by default, and types must be explicit.

    // Variable declarations
    const PI = 3.14159;        // Compile-time constant
    var count: u32 = 0;        // Mutable variable (u32 = unsigned 32-bit int)
    const name = "Zig";        // Type inferred as []const u8
    
    // Type system
    var a: i32 = 42;           // Signed 32-bit integer
    var b: f64 = 3.14;         // 64-bit float
    var c: bool = true;        // Boolean
    var d: u8 = 'A';           // Unicode codepoint (ASCII char)
    
    // Arrays and slices
    const arr: [3]i32 = .{ 1, 2, 3 };     // Fixed-size array
    const slice = arr[0..2];              // Slice (dynamic view)
    
    // Structs
    const Point = struct {
        x: f64,
        y: f64,
        
        fn distance(self: Point, other: Point) f64 {
            const dx = other.x - self.x;
            const dy = other.y - self.y;
            return @sqrt(dx * dx + dy * dy);
        }
    };
    
    const p1 = Point{ .x = 0, .y = 0 };
    const p2 = Point{ .x = 3, .y = 4 };
    const dist = p1.distance(p2);         // Returns 5.0
    
    // Enums
    enum Color {
        red,
        green,
        blue,
    }
    
    const c: Color = .red;
    
    // Switch expressions
    fn colorName(color: Color) []const u8 {
        return switch (color) {
            .red => "Red",
            .green => "Green",
            .blue => "Blue",
        };
    }
  6. Step 6

    Error Handling

    Zig uses explicit error unions instead of exceptions. Errors must be handled at every call site using the try operator or errdefer.

    // Error unions
    fn readFile(path: []const u8) ![]u8 {
        // Returns either []u8 (success) or an error
        return allocator.alloc(u8, size);
    }
    
    // Using try to propagate errors
    fn processFile(path: []const u8) !void {
        const data = try readFile(path);      // Propagates error
        defer allocator.free(data);           // Cleanup on function exit
        
        // Process data...
    }
    
    // Error handling with catch
    const result = riskyOperation() catch |\err| switch (err) {
        error.OutOfMemory => return 0,
        error.FileNotFound => return 1,
        else => return 2,
    };
    
    // errdefer for cleanup
    fn allocateAndUse() !void {
        const ptr = try allocator.alloc(u8, 100);
        errdefer allocator.free(ptr);   // Runs if any error after this
        
        try doSomething(ptr);           // If this fails, errdefer runs
        // If we reach here, no error occurred
        allocator.free(ptr);            // Manual cleanup
    }
    
    // else defer for normal cleanup
    fn safeOperation() void {
        const ptr = allocator.alloc(u8, 100) orelse return;
        defer allocator.free(ptr);      // Always runs at function exit
        
        use(ptr);
        // ptr is freed here automatically
    }
  7. Step 7

    Memory Management

    Zig provides manual memory management with allocator abstraction. There's no garbage collector, giving you full control over memory.

    // General purpose allocator
    const std = @import("std");
    
    pub fn main() !void {
        var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        defer _ = gpa.deinit();
        
        const allocator = gpa.allocator();
        
        // Allocate memory
        const ptr = try allocator.alloc(u8, 1024);
        defer allocator.free(ptr);    // Free on scope exit
        
        // Allocate with alignment
        const aligned = try allocator.alignedAlloc(u8, 16, 1024);
        defer allocator.alignedFree(aligned);
        
        // Create strings
        const str = try allocator.dupe(u8, "Hello");
        defer allocator.free(str);
        
        // Automatic arena allocator (for scope-limited allocations)
        var arena = std.heap.ArenaAllocator.init(allocator);
        defer arena.deinit();
        
        const arena_alloc = arena.allocator();
        const data = try arena_alloc.alloc(u8, 1000);
        // No need to free - arena.deinit() frees everything
        
        // Page allocator (for large allocations)
        var page_allocator = std.heap.PageAllocator{};
        defer page_allocator.deinit();
    }
  8. Step 8

    C Interoperability

    Zig can import C headers directly and link with C libraries. This is one of Zig's strongest features for gradual adoption.

    // Import C header
    const c = @cImport({
        @cInclude("stdio.h"),
        @cInclude("stdlib.h"),
    });
    
    pub fn main() void {
        // Call C functions
        c.printf("Hello from C!\n");
        
        // Use C types
        var num: c_int = 42;
        c.free(num);
    }
    
    // Link with C library
    zig build-exe main.zig -lc
    
    // Import generated bindings
    c const libc = @cImport({
        @cInclude("libc.h");  // Zig provides stdlibc.zig
    });
    
    // Use Zig's standard C bindings
    const std = @import("std");
    const c = std.c;
    
    c.printf("Using std.c\n");
    
    // Compile C code with Zig
    zig cc main.c -o program
    zig c++ main.cpp -o program
    zig ar rcs libfoo.a foo.o
    zig cppcheck file.c
    
    // C header generation
    zig translate-c foo.h -o foo.zig
  9. Step 9

    Compile-Time Execution

    Zig can run code at compile-time using comptime. This enables powerful metaprogramming without a preprocessor.

    // Compile-time constants
    const pi = @constEval(floatingPoint, .{ 3.14159 });
    
    // Compile-time functions
    fn computeAtCompileTime(n: comptime usize) []const u8 {
        var result = "";
        var i: usize = 0;
        while (i < n) : (i += 1) {
            result = result ++ "x";
        }
        return result;
    }
    
    const repeated = computeAtCompileTime(5);  // "xxxxx" computed at compile-time
    
    // Compile-time type generation
    fn Vector(comptime T: type, comptime dimensions: usize) type {
        return struct {
            data: [dimensions]T,
            
            fn init(self: *@This(), value: T) void {
                for (&self.data) |*d| *
                    d.* = value;
            }
        };
    }
    
    const Vec3 = Vector(f32, 3);   // 3D vector of floats
    const Vec4 = Vector(f32, 4);   // 4D vector of floats
    const Vec3Int = Vector(i32, 3); // 3D vector of integers
    
    // Compile-time conditionals
    if (comptime std.os.tag == .linux) {
        // Linux-specific code
    } else if (comptime std.os.tag == .windows) {
        // Windows-specific code
    }
    
    // Inline assembly at compile-time
    const asm_result = @asm("mov {0}, {1}", .{ result, value });
  10. Step 10

    Standard Library

    Zig's standard library provides utilities for I/O, collections, strings, math, and more. All code is visible and auditable.

    // Import standard library
    const std = @import("std");
    
    // I/O operations
    pub fn main() !void {
        const stdout = std.io.getStdOut().writer();
        const stdin = std.io.getStdIn().reader();
        
        try stdout.print("Enter name: ", .{});
        var buffer: [100]u8 = undefined;
        const input = try stdin.readUntilDelimiterOrEof(&buffer, '\n');
        
        try stdout.print("Hello, {}!\n", .{input});
        
        // File operations
        const file = try std.fs.cwd().createFile("output.txt", .{});
        defer file.close();
        try file.write("Hello, File!\n");
        
        // Reading files
        const content = try std.fs.cwd().readAllAlloc(std.heap.page_allocator, "input.txt");
        defer std.heap.page_allocator.free(content);
    }
    
    // Collections
    const ArrayList = std.ArrayList;
    const StringHashMap = std.StringHashMap;
    const BinaryHeap = std.BinaryHeap;
    
    // String utilities
    const strings = std.mem.String;
    const ascii = std.ascii;
    
    // Hash
    const hash = std.hash;
    const sha256 = crypto.hash.sha2;
    
    // Math
    const math = std.math;
    const result = math.sqrt(f64, 16.0);
    
    // Testing
    test "addition" {
        const a = 5;
        const b = 3;
        try std.testing.expect(a + b == 8);
    }
  11. Step 11

    Testing

    Zig has built-in testing support. Tests are run with zig test and can be inline or in separate files.

    // Inline tests (same file)
    fn add(a: i32, b: i32) i32 {
        return a + b;
    }
    
    test "addition" {
        try std.testing.expect(add(2, 3) == 5);
        try std.testing.expect(add(-1, 1) == 0);
    }
    
    test "subtraction" {
        const result = sub(5, 3);
        try std.testing.expectEqual(@as(i32, 2), result);
    }
    
    // Table-driven tests
    test "arithmetic" {
        const tests = _[
            .{ .a = 1, .b = 2, .expected = 3 },
            .{ .a = 0, .b = 0, .expected = 0 },
            .{ .a = -1, .b = 1, .expected = 0 },
        ];
        
        for (tests) |test_case| {
            try std.testing.expectEqual(
                test_case.expected,
                add(test_case.a, test_case.b)
            );
        }
    }
    
    // Run tests
    zig test src/main.zig              # Test single file
    zig build test                     # Run build test step
    zig test --summary all            # Show all test results
    
    // Test coverage
    zig build-obj main.zig -fcoverage-sanitizer
    llvm-profdata merge -output=default.profdata default_%m_%p.profraw
    llvm-cov report main -instr-profile=default.profdata
  12. Step 12

    Package Management

    Zig has a built-in package manager. Dependencies are declared in build.zon and resolved automatically.

    # Package manifest (build.zon)
    {
        "name": "my-project",
        "version": "0.1.0",
        "path": "../",
        "dependencies": .{
            "mustache": .{
                "url": "https://github.com/ziglang/mustache.zig/archive/refs/tags/v1.0.0.tar.gz",
                "hash": "abc123...",
            },
        },
    }
    
    # In build.zig:
    const mustache = b.dependency("mustache", .{ ."version" = "~>1.0.0" });
    lib.root_module.addImport("mustache", mustache.module("."));
    
    # Add dependency from local path
    const local_dep = b.dependency("local", .{
        .path = "../local-package",
    });
    
    # Publish package
    # 1. Create build.zon with dependencies
    # 2. Upload source to GitHub/GitLab
    # 3. Reference in other projects via URL
    
    # Note: Zig package manager is still evolving
    # Check https://zigls.dev for available packages
  13. Step 13

    Cross-Compilation

    Zig has built-in cross-compilation support. No need for separate toolchains or complex setup.

    # List supported targets
    zig targets list
    
    # Cross-compile for Windows from Linux
    zig build-exe main.zig -target x86_64-windows -o app.exe
    
    # Cross-compile for macOS from Linux
    zig build-exe main.zig -target x86_64-macos -o app
    
    # Cross-compile for ARM
    zig build-exe main.zig -target armv7a-none-linux-gnueabihf -o app_arm
    
    # Cross-compile for WebAssembly
    zig build-exe main.zig -target wasm31-freestanding -o app.wasm
    
    # Check available ABIs
    zig targets list | grep windows
    
    # Create toolchain for specific target
    zig archiver create-toolchain x86_64-linux-gnu
    
    # In build.zig:
    const target = b.target(
        .{
            .cpu_arch = .x86_64,
            .os_tag = .windows,
            .abi = .gnu,
        }
    );
  14. Step 14

    Performance Optimization

    Zig provides multiple optimization levels and tools for profiling and optimizing your code.

    # Optimization levels
    zig build-exe main.zig -O Debug        # No optimization, debug info
    zig build-exe main.zig -O ReleaseSafe  # Optimizations, some checks
    zig build-exe main.zig -O ReleaseFast  # Maximum speed optimizations
    zig build-exe main.zig -O ReleaseSmall # Size optimizations
    
    # Link-time optimizations
    zig build-exe main.zig -O ReleaseFast -flto
    
    # Strip symbols
    zig build-exe main.zig -O ReleaseFast -fstrip=all
    
    # Profile-guided optimization
    zig build-exe main.zig -fprofile-generate
    ./app  # Run to collect profile
    zig build-exe main.zig -fprofile-use    # Build with profile
    
    # Performance analysis
    cachegrind (Valgrind) for cycle counting
    perf for Linux performance analysis
    flamegraph for visualization
    
    // In code: use @call(.fast) for inlining
    const result = @call(.fast, func, .{ arg1, arg2 });
    
    // Use @compileLog for debugging compile-time
    @compileLog("Value: ", value, "\n");
  15. Step 15

    Best Practices

    Learn common patterns, anti-patterns, and best practices for writing idiomatic Zig code.

    ## Common Patterns
    
    1. Use `try` for error propagation, not `catch` everywhere
    2. Use `defer` for cleanup, `errdefer` for error cleanup
    3. Prefer `comptime` for compile-time computations
    4. Use allocators explicitly, don't hide allocations
    5. Make variables `const` by default, `var` when needed
    6. Use slices instead of arrays when size is dynamic
    7. Use `std.testing.expectEqual` for readable tests
    
    ## Anti-Patterns
    
    1. Don't use `undefined` without initializing
    2. Don't ignore errors with `catch {}`
    3. Don't use `@as()` for unsafe casts
    4. Don't create memory leaks with forgotten `defer`
    5. Don't use `@compileLog()` in production code
    
    ## Code Organization
    
    - Put public functions at the top of files
    - Use `fn` for public, `const fn` for comptime functions
    - Group related types in structs
    - Use namespaces (structs with only static methods)
    - Separate platform-specific code with comptime checks
    
    ## Documentation
    
    /// This is a function doc comment
    fn myFunction(arg: Type) ReturnType {
        // Implementation
    }
    
    // Inline comments explain implementation details
    const value = 42; // Magic number for XYZ
  16. Step 16

    Resources

    Complete list of official and community resources for learning and using Zig.

    ## Official Resources
    
    Zig Website: https://ziglang.org
    Documentation: https://ziglang.org/documentation/master/
    Language Reference: https://ziglang.org/documentation/master/#Language-Reference
    Build System: https://ziglang.org/documentation/master/#Build-System
    GitHub (Mirror): https://github.com/ziglang/zig
    Codeberg (Primary): https://codeberg.org/ziglang/zig
    Discord: https://discord.gg/zig
    Mailing List: https://lists.ziglang.org
    
    ## Learning Resources
    
    Learn Zig: https://ziglearn.org
    Zig Book (Unofficial): https://zigbook.readthedocs.io
    Zig in Depth: https://ziglex.org
    Zig By Example: https://github.com/zig-examples
    Awesome Zig: https://github.com/cornelk/awesome-zig
    
    ## Community
    
    Zig Forums: https://zigforum.org
    Stack Overflow: https://stackoverflow.com/questions/tagged/zig
    Zig Packages: https://zigls.dev
    Zig Standard Library: https://github.com/ziglang/zig/tree/master/lib/std
    
    ## Tools
    
    zls (Zig Language Server): https://github.com/ziglang/zls
    zigfmt (Formatter): Built into Zig compiler
    zig repl: Interactive REPL (experimental)
    
    ## Tutorials
    
    Zig Crash Course: https://zigcrashcourse.com
    Zig in 100 Seconds: Various YouTube channels
    Building a Game in Zig: https://gamedevguide.com

Feature requests

Sign in to suggest features or vote on existing ones.

No feature requests yet.

Discussion

0 people marked this as worked·Sign in to mark your own.

Sign in to join the discussion.

No comments yet.