Zig Programming Language: Panduan Lengkap dari Dasar hingga Mahir

Zig adalah bahasa pemrograman systems-level yang menawarkan kinerja tinggi, keamanan memori, dan kesederhanaan tanpa garbage collector. Pelajari comptime, allocators, error handling, dan banyak lagi.

1. Pengenalan Zig

Zig adalah bahasa pemrograman systems-level yang dirancang oleh Andrew Kelley pada tahun 2015. Zig hadir sebagai alternatif C yang lebih modern, aman, dan produktif tanpa mengorbankan performa. Bahasa ini tidak memiliki garbage collector, tidak memiliki preprocessor, dan tidak memiliki hidden control flow.

Filosofi utama Zig meliputi:

  • Konsistensi — Tidak ada hidden behavior yang bisa mengejutkan programmer
  • Kejelasan — Kode harus mudah dibaca dan dipahami
  • Performa — Menghasilkan binary yang sangat cepat
  • Kesesuaian dengan C — Bisa memanggil kode C langsung tanpa FFI
💡 Mengapa Zig?

Zig bukan hanya bahasa baru, tapi juga bisa menjadi "drop-in replacement" untuk compiler C/C++. Zig dapat meng-compile kode C, C++, dan Zig dalam satu proyek menggunakan Zig Build System.

Keunggulan Zig Dibanding Bahasa Lain

Fitur Zig C Rust Go
Comptime ✅ Ya ❌ Preprocessor ✅ Macros
C Interop ✅ Native - ⚠️ FFI ⚠️ CGo
Garbage Collector ❌ Tidak ❌ Tidak ❌ Tidak ✅ Ya
Build System ✅ Terintegrasi ❌ Eksternal ✅ Cargo ✅ Go modules
Compile Speed Cepat Cepat Lambat Cepat

2. Instalasi dan Setup

Menginstal Zig sangat mudah. Zig tersedia untuk berbagai platform termasuk Windows, macOS, dan Linux.

Instalasi di Berbagai Platform

Bash
# Linux / macOS (menggunakan snap)
sudo snap install zig --classic --beta

# macOS (menggunakan Homebrew)
brew install zig

# Windows (menggunakan Scoop)
scoop install zig

# Atau download langsung dari ziglang.org
# https://ziglang.org/download/

# Verifikasi instalasi
zig version
# Output: 0.13.0 (atau versi terbaru)

Setup Editor

Untuk pengalaman coding yang optimal, gunakan extension berikut:

  • VS Code — Extension "Zig" oleh ziglang
  • Neovim — Plugin zigtools/zls
  • Zed — Support Zig built-in

Membuat Proyek Pertama

Bash
# Buat proyek baru
mkdir hello-zig && cd hello-zig
zig init-exe

# Struktur proyek akan seperti ini:
# hello-zig/
# ├── build.zig
# ├── build.zig.zon
# └── src/
#     └── main.zig

# Build dan jalankan
zig build run

3. Dasar-Dasar Zig

Mari kita pelajari sintaks dasar Zig mulai dari program "Hello World".

Hello World

src/main.zig
const std = @import("std");

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

Variabel dan Konstanta

Zig membedakan antara variabel (bisa diubah) dan konstanta (tidak bisa diubah):

variabel.zig
const std = @import("std");

pub fn main() void {
    // Konstanta - tidak bisa diubah
    const nama: []const u8 = "BeebaneLabs";
    const umur: u32 = 25;

    // Variabel - bisa diubah
    var skor: i32 = 100;
    skor += 50; // OK

    // Type inference - Zig bisa mendeteksi tipe secara otomatis
    var otomatis = 42; // tipe: comptime_int
    var teks = "Halo"; // tipe: *const [4:0]u8

    // Underscore untuk membuang nilai
    _ = otomatis;
    _ = teks;

    std.debug.print("Nama: {s}, Umur: {d}, Skor: {d}\n", .{ nama, umur, skor });
}

Operators dan Expressions

operators.zig
const std = @import("std");

pub fn main() void {
    // Aritmatika
    const a: i32 = 10;
    const b: i32 = 3;

    const tambah = a + b;      // 13
    const kurang = a - b;      // 7
    const kali = a * b;        // 30
    const bagi = @divExact(a, b); // error jika tidak habis dibagi
    const bagi_floor = @divFloor(a, b); // 3
    const sisa = @mod(a, b);   // 1

    // Bitwise operators
    const x: u8 = 0b1010;
    const y: u8 = 0b1100;
    const AND = x & y;         // 0b1000 = 8
    const OR = x | y;          // 0b1110 = 14
    const XOR = x ^ y;         // 0b0110 = 6

    // Saturating arithmetic
    const max_u8: u8 = 255;
    const saturated = std.math.add(u8, max_u8, 1) catch max_u8;

    std.debug.print("Tambah: {d}, Kali: {d}\n", .{ tambah, kali });
    std.debug.print("AND: {b}, OR: {b}\n", .{ AND, OR });
    _ = bagi;
    _ = bagi_floor;
    _ = sisa;
    _ = XOR;
    _ = saturated;
}

Control Flow

control_flow.zig
const std = @import("std");

pub fn main() void {
    // If expressions (bukan statements!)
    const nilai: i32 = 85;
    const grade = if (nilai >= 90)
        "A"
    else if (nilai >= 80)
        "B"
    else if (nilai >= 70)
        "C"
    else
        "D";

    std.debug.print("Grade: {s}\n", .{grade});

    // While loop
    var i: u32 = 0;
    var total: u32 = 0;
    while (i < 10) : (i += 1) {
        total += i;
    }
    std.debug.print("Total 0-9: {d}\n", .{total});

    // For loop dengan range
    var jumlah: u32 = 0;
    for (0..10) |angka| {
        jumlah += angka;
    }
    std.debug.print("Jumlah: {d}\n", .{jumlah});

    // Labeled blocks
    const hasil = blk: {
        const x = 10;
        const y = 20;
        break :blk x + y;
    };
    std.debug.print("Hasil block: {d}\n", .{hasil});
}

Functions

functions.zig
const std = @import("std");

// Fungsi biasa
fn tambah(a: i32, b: i32) i32 {
    return a + b;
}

// Fungsi dengan error union
fn bagi(a: f64, b: f64) !f64 {
    if (b == 0) return error.DivisionByZero;
    return a / b;
}

// Generic functions menggunakan comptime
fn max(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

// Fungsi dengan multiple return values
fn minMax(data: []const i32) struct { min: i32, max: i32 } {
    var min_val = data[0];
    var max_val = data[0];
    for (data[1..]) |val| {
        if (val < min_val) min_val = val;
        if (val > max_val) max_val = val;
    }
    return .{ .min = min_val, .max = max_val };
}

pub fn main() !void {
    std.debug.print("3 + 5 = {d}\n", .{tambah(3, 5)});

    const hasil = try bagi(10.0, 3.0);
    std.debug.print("10 / 3 = {d}\n", .{hasil});

    std.debug.print("max(10, 20) = {d}\n", .{max(i32, 10, 20)});

    const data = [_]i32{ 5, 2, 8, 1, 9, 3 };
    const mm = minMax(&data);
    std.debug.print("Min: {d}, Max: {d}\n", .{ mm.min, mm.max });
}

4. Tipe Data dan Variabel

Zig memiliki sistem tipe yang sangat ekspresif dan kuat. Berikut adalah tipe-tipe utama di Zig:

Integer Types

integers.zig
const std = @import("std");

pub fn main() void {
    // Unsigned integers: u0, u1, u2, ..., u65535
    const a: u8 = 255;      // 0 sampai 255
    const b: u16 = 65535;   // 0 sampai 65535
    const c: u32 = 4294967295;
    const d: u64 = 18446744073709551615;

    // Signed integers: i0, i1, i2, ..., i65535
    const e: i8 = -128;     // -128 sampai 127
    const f: i16 = -32768;
    const g: i32 = -2147483648;
    const h: i64 = -9223372036854775808;

    // Arbitrary precision
    const besar: u128 = 340282366920938463463374607431768211455;

    // Byte type
    const byte: u8 = 'A'; // 65

    _ = a; _ = b; _ = c; _ = d;
    _ = e; _ = f; _ = g; _ = h;
    _ = besar; _ = byte;
}

Structs dan Enums

structs.zig
const std = @import("std");

// Struct definition
const Mahasiswa = struct {
    nama: []const u8,
    nim: u32,
    ipk: f32,

    // Method (hanya function yang mengambil self sebagai parameter)
    fn toString(self: Mahasiswa) void {
        std.debug.print("Nama: {s}, NIM: {d}, IPK: {d:.2}\n",
            .{ self.nama, self.nim, self.ipk });
    }

    fn isLulus(self: Mahasiswa) bool {
        return self.ipk >= 2.5;
    }
};

// Enum
const Warna = enum {
    merah,
    hijau,
    biru,
    kuning,

    fn toHex(self: Warna) []const u8 {
        return switch (self) {
            .merah => "#FF0000",
            .hijau => "#00FF00",
            .biru => "#0000FF",
            .kuning => "#FFFF00",
        };
    }
};

// Tagged union (enum + union)
const Bentuk = union(enum) {
    lingkaran: f64,
    persegi: f64,
    segitiga: struct { alas: f64, tinggi: f64 },

    fn luas(self: Bentuk) f64 {
        return switch (self) {
            .lingkaran => |r| std.math.pi * r * r,
            .persegi => |s| s * s,
            .segitiga => |t| 0.5 * t.alas * t.tinggi,
        };
    }
};

pub fn main() void {
    const mhs = Mahasiswa{
        .nama = "Andi Pratama",
        .nim = 2024001,
        .ipk = 3.75,
    };
    mhs.toString();
    std.debug.print("Lulus: {}\n", .{mhs.isLulus()});

    const warna = Warna.biru;
    std.debug.print("Warna: {s}\n", .{warna.toHex()});

    const bentuk = Bentuk{ .lingkaran = 5.0 };
    std.debug.print("Luas lingkaran: {d:.2}\n", .{bentuk.luas()});
}

Arrays dan Slices

arrays.zig
const std = @import("std");

pub fn main() void {
    // Fixed-size array
    const angka = [_]u32{ 10, 20, 30, 40, 50 };

    // Array dengan pattern
    const nol = [_]u8{0} ** 10; // 10 elemen bernilai 0

    // Multi-dimensional array
    const matriks = [3][3]f32{
        .{ 1.0, 0.0, 0.0 },
        .{ 0.0, 1.0, 0.0 },
        .{ 0.0, 0.0, 1.0 },
    };

    // Slice (pointer + length)
    const slice = angka[1..4]; // [20, 30, 40]

    // Iterasi dengan for
    for (angka, 0..) |nilai, index| {
        std.debug.print("angka[{d}] = {d}\n", .{ index, nilai });
    }

    // Compile-time array manipulation
    const kuadrat = comptime blk: {
        var result: [5]u32 = undefined;
        for (&result, 0..) |*r, i| {
            r.* = i * i;
        }
        break :blk result;
    };
    std.debug.print("Kuadrat: {any}\n", .{kuadrat});

    _ = nol;
    _ = matriks;
    _ = slice;
}

5. Comptime (Compile-Time Execution)

Comptime adalah fitur unggulan Zig yang memungkinkan eksekusi kode saat compile time. Ini berbeda dari preprocessor di C atau macro di Rust — comptime menjalankan kode Zig yang sama persis dengan runtime code.

🎯 Keunggulan Comptime:

Comptime memungkinkan Anda menulis kode generik tanpa runtime overhead. Semua perhitungan dilakukan saat kompilasi, sehingga binary yang dihasilkan sangat efisien.

Comptime Variables dan Expressions

comptime_basics.zig
const std = @import("std");

// Compile-time factorial
fn comptimeFactorial(comptime n: u32) u32 {
    if (n <= 1) return 1;
    return n * comptimeFactorial(n - 1);
}

// Compile-time Fibonacci
fn comptimeFib(comptime n: u32) u32 {
    if (n <= 1) return n;
    return comptimeFib(n - 1) + comptimeFib(n - 2);
}

pub fn main() void {
    // Semua perhitungan ini terjadi saat compile time!
    comptime {
        const fak_10 = comptimeFactorial(10);
        std.debug.assert(fak_10 == 3628800);

        const fib_20 = comptimeFib(20);
        std.debug.assert(fib_20 == 6765);
    }

    // Comptime string manipulation
    const pesan = comptime blk: {
        const angka = 42;
        var buf: [100]u8 = undefined;
        const len = std.fmt.bufPrint(&buf, "Jawabannya adalah {d}", .{angka}) catch unreachable;
        break :blk buf[0..len.len];
    };

    std.debug.print("{s}\n", .{pesan});
}

Generic Types dengan Comptime

generics.zig
const std = @import("std");

// Generic Stack
fn Stack(comptime T: type, comptime capacity: usize) type {
    return struct {
        const Self = @This();

        items: [capacity]T = undefined,
        len: usize = 0,

        fn push(self: *Self, item: T) !void {
            if (self.len >= capacity) return error.StackOverflow;
            self.items[self.len] = item;
            self.len += 1;
        }

        fn pop(self: *Self) ?T {
            if (self.len == 0) return null;
            self.len -= 1;
            return self.items[self.len];
        }

        fn peek(self: *Self) ?T {
            if (self.len == 0) return null;
            return self.items[self.len - 1];
        }
    };
}

pub fn main() !void {
    // Stack of integers
    var int_stack = Stack(i32, 10){};
    try int_stack.push(10);
    try int_stack.push(20);
    try int_stack.push(30);

    while (int_stack.pop()) |val| {
        std.debug.print("Pop: {d}\n", .{val});
    }

    // Stack of strings
    var str_stack = Stack([]const u8, 5){};
    try str_stack.push("Hello");
    try str_stack.push("World");
    std.debug.print("Peek: {s}\n", .{str_stack.peek().?});
}

Comptime Type Reflection

reflection.zig
const std = @import("std");

fn printStructInfo(comptime T: type) void {
    const info = @typeInfo(T);
    switch (info) {
        .Struct => |s| {
            std.debug.print("Struct: {s}\n", .{@typeName(T)});
            std.debug.print("Fields: {d}\n", .{s.fields.len});
            inline for (s.fields) |field| {
                std.debug.print("  - {s}: {s}\n", .{
                    field.name,
                    @typeName(field.type),
                });
            }
        },
        else => std.debug.print("Bukan struct: {s}\n", .{@typeName(T)}),
    }
}

const Config = struct {
    host: []const u8,
    port: u16,
    debug: bool,
    max_connections: u32,
};

pub fn main() void {
    comptime printStructInfo(Config);
}

6. Memory Allocators

Salah satu fitur paling penting di Zig adalah pendekatannya terhadap manajemen memori. Zig tidak memiliki GC, tetapi menyediakan allocator interface yang fleksibel dan eksplicit.

⚠️ Penting:

Di Zig, semua alokasi memori harus melalui allocator yang di-pass secara eksplisit. Ini membuat jejak alokasi memori sangat jelas dan mudah di-debug.

Jenis Allocator

allocators.zig
const std = @import("std");

pub fn main() !void {
    // 1. General Purpose Allocator (untuk production)
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Alokasi buffer
    const buffer = try allocator.alloc(u8, 1024);
    defer allocator.free(buffer);

    // 2. Arena Allocator (free semua sekaligus)
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const arena_alloc = arena.allocator();

    // Banyak alokasi, dealokasi otomatis saat arena_deinit
    const data1 = try arena_alloc.alloc(u32, 100);
    const data2 = try arena_alloc.alloc(u8, 500);
    _ = data1;
    _ = data2;

    // 3. Fixed Buffer Allocator (stack-allocated)
    var buf: [1024]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buf);
    const fba_alloc = fba.allocator();

    const msg = try fba_alloc.dupe(u8, "Hello from FBA!");
    std.debug.print("{s}\n", .{msg});

    // 4. Page Allocator (system page allocator)
    const page_alloc = std.heap.page_allocator;
    const big_buf = try page_alloc.alloc(u8, 4096);
    defer page_alloc.free(big_buf);

    std.debug.print("Buffer sizes: {d}, {d}, {d}\n", .{
        buffer.len, msg.len, big_buf.len,
    });
}

Dynamic Arrays dengan Allocator

arraylist.zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // ArrayList - dynamic array
    var list = std.ArrayList(i32).init(allocator);
    defer list.deinit();

    // Tambah elemen
    for (0..100) |i| {
        try list.append(@intCast(i * i));
    }

    std.debug.print("Jumlah elemen: {d}\n", .{list.items.len});
    std.debug.print("Elemen pertama: {d}\n", .{list.items[0]});
    std.debug.print("Elemen terakhir: {d}\n", .{list.items[list.items.len - 1]});

    // HashMap
    var map = std.StringHashMap(u32).init(allocator);
    defer map.deinit();

    try map.put("apel", 5);
    try map.put("jeruk", 3);
    try map.put("mangga", 8);

    if (map.get("apel")) |jumlah| {
        std.debug.print("Jumlah apel: {d}\n", .{jumlah});
    }

    // Iterasi HashMap
    var iter = map.iterator();
    while (iter.next()) |entry| {
        std.debug.print("  {s}: {d}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
    }
}

7. Error Handling

Zig menggunakan error unions untuk error handling — pendekatan yang lebih eksplisit dibanding exception di bahasa lain.

Error Unions

errors.zig
const std = @import("std");

// Custom error set
const FileError = error{
    NotFound,
    PermissionDenied,
    DiskFull,
    InvalidPath,
};

// Error union return type
fn readFile(path: []const u8) FileError![]const u8 {
    if (path.len == 0) return error.InvalidPath;
    if (std.mem.eql(u8, path, "/secret")) return error.PermissionDenied;
    if (std.mem.eql(u8, path, "/missing")) return error.NotFound;
    return "file contents here";
}

// try operator - propagate error
fn processFile(path: []const u8) !void {
    const content = try readFile(path);
    std.debug.print("Content: {s}\n", .{content});
}

// catch operator - handle error
fn safeRead(path: []const u8) void {
    const content = readFile(path) catch |err| {
        std.debug.print("Error: {}\n", .{err});
        return;
    };
    std.debug.print("Content: {s}\n", .{content});
}

// Error union dengan switch
fn handleResult(result: FileError![]const u8) void {
    if (result) |content| {
        std.debug.print("Success: {s}\n", .{content});
    } else |err| {
        switch (err) {
            error.NotFound => std.debug.print("File tidak ditemukan\n", .{}),
            error.PermissionDenied => std.debug.print("Akses ditolak\n", .{}),
            error.DiskFull => std.debug.print("Disk penuh\n", .{}),
            error.InvalidPath => std.debug.print("Path tidak valid\n", .{}),
        }
    }
}

pub fn main() void {
    safeRead("/valid/path");
    safeRead("/missing");
    safeRead("/secret");
    safeRead("");
}

Error Handling Patterns

error_patterns.zig
const std = @import("std");

fn parseU32(input: []const u8) !u32 {
    return std.fmt.parseInt(u32, input, 10) catch return error.ParseError;
}

fn validateAge(input: []const u8) !u8 {
    const age = parseU32(input) orelse return error.ParseError;
    if (age > 150) return error.InvalidAge;
    return @intCast(age);
}

// Error set merging
const AppError = FileError || error{OutOfMemory};

const FileError = error{
    FileNotFound,
    InvalidFormat,
};

pub fn main() void {
    // orelse - unwrap atau default
    const val = parseU32("42") orelse 0;
    std.debug.print("Value: {d}\n", .{val});

    // catch dengan default
    const safe_val = parseU32("abc") catch 0;
    std.debug.print("Safe value: {d}\n", .{safe_val});

    // errdefer - cleanup saat error
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const data = allocator.alloc(u8, 100) catch |err| {
        std.debug.print("Alokasi gagal: {}\n", .{err});
        return;
    };
    defer allocator.free(data);

    std.debug.print("Alokasi berhasil: {d} bytes\n", .{data.len});
    _ = val;
    _ = safe_val;
}

8. Build System

Zig memiliki build system terintegrasi yang ditulis dalam Zig sendiri. Ini menggantikan kebutuhan akan Make, CMake, atau build tools lainnya.

Konfigurasi build.zig

build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    // Standard target options
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // Executable
    const exe = b.addExecutable(.{
        .name = "my-app",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    b.installArtifact(exe);

    // Run step
    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());

    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    // Unit tests
    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);
    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&run_unit_tests.step);
}

Menambah Dependencies

build.zig.zon
// build.zig.zon - file dependencies Zig
.{
    .name = "my-app",
    .version = "0.1.0",
    .dependencies = .{
        .@"zig-network" = .{
            .url = "https://github.com/MasterQ32/zig-network/archive/refs/heads/master.tar.gz",
            .hash = "1220abc...",
        },
    },
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}

Cross Compilation

Cross Compile Commands
# Compile untuk Linux x86_64
zig build -Dtarget=x86_64-linux-gnu

# Compile untuk Windows
zig build -Dtarget=x86_64-windows-gnu

# Compile untuk macOS ARM
zig build -Dtarget=aarch64-macos

# Compile untuk WebAssembly
zig build -Dtarget=wasm32-wasi

# Compile dengan optimasi
zig build -Doptimize=ReleaseFast
zig build -Doptimize=ReleaseSafe
zig build -Doptimize=ReleaseSmall
zig build -Doptimize=Debug

9. C Interop

Salah satu keunggulan terbesar Zig adalah kemampuannya untuk langsung memanggil kode C tanpa FFI wrapper. Zig bahkan bisa meng-include header file C secara langsung.

Menggunakan C Libraries

c_interop.zig
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("math.h");
    @cInclude("string.h");
});

const std = @import("std");

pub fn main() void {
    // Memanggil C functions langsung
    _ = c.printf("Hello from C printf: %d\n", 42);

    // Menggunakan C math library
    const result = c.sqrt(144.0);
    std.debug.print("sqrt(144) = {d}\n", .{result});

    // Menggunakan C string functions
    var buf: [100]u8 = undefined;
    _ = c.strcpy(&buf, "Hello from C!");
    _ = c.strcat(&buf, " Concatenated!");
    std.debug.print("String: {s}\n", .{buf});

    // Menggunakan sin, cos, tan dari C math
    const angle: f64 = std.math.pi / 4.0; // 45 degrees
    std.debug.print("sin(45°) = {d}\n", .{c.sin(angle)});
    std.debug.print("cos(45°) = {d}\n", .{c.cos(angle)});
}

Memanggil Zig dari C

export.zig
// Fungsi ini bisa dipanggil dari C
export fn zig_add(a: i32, b: i32) i32 {
    return a + b;
}

// Fungsi dengan calling convention C
export fn zig_greet(name: [*:0]const u8) void {
    const std = @import("std");
    std.debug.print("Halo, {s}!\n", .{name});
}

// Struct yang compatible dengan C
export const Point = extern struct {
    x: f64,
    y: f64,
};

export fn zig_distance(p1: Point, p2: Point) f64 {
    const dx = p1.x - p2.x;
    const dy = p1.y - p2.y;
    return @sqrt(dx * dx + dy * dy);
}

10. Concurrency di Zig

Zig menyediakan primitive concurrency yang rendah-level dan fleksibel tanpa runtime yang berat.

Threads

threads.zig
const std = @import("std");

var counter: u32 = 0;
var mutex = std.Thread.Mutex{};

fn worker(id: u32) void {
    var i: u32 = 0;
    while (i < 1000) : (i += 1) {
        mutex.lock();
        counter += 1;
        mutex.unlock();
    }
    std.debug.print("Worker {d} selesai\n", .{id});
}

pub fn main() !void {
    // Buat beberapa threads
    var threads: [4]std.Thread = undefined;

    for (&threads, 0..) |*t, i| {
        t.* = try std.Thread.spawn(.{}, worker, .{@as(u32, @intCast(i))});
    }

    // Tunggu semua threads selesai
    for (&threads) |*t| {
        t.join();
    }

    std.debug.print("Counter final: {d}\n", .{counter});
}

Channels (simplified)

channels.zig
const std = @import("std");

// Atomic operations
var shared_value: u32 = 0;
var done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);

fn producer() void {
    var i: u32 = 0;
    while (i < 10) : (i += 1) {
        shared_value = i;
        std.debug.print("Produced: {d}\n", .{i});
        std.time.sleep(100_000_000); // 100ms
    }
    done.store(true, .seq_cst);
}

fn consumer() void {
    while (!done.load(.seq_cst)) {
        std.debug.print("Consumed: {d}\n", .{shared_value});
        std.time.sleep(150_000_000); // 150ms
    }
}

pub fn main() !void {
    const producer_thread = try std.Thread.spawn(.{}, producer, .{});
    const consumer_thread = try std.Thread.spawn(.{}, consumer, .{});

    producer_thread.join();
    consumer_thread.join();
}

11. Proyek Praktis: HTTP Server Sederhana

Mari kita buat HTTP server sederhana menggunakan Zig networking.

src/server.zig
const std = @import("std");

const Response = struct {
    status: u16,
    body: []const u8,

    fn format(self: Response, allocator: std.mem.Allocator) ![]u8 {
        return std.fmt.allocPrint(allocator,
            \\HTTP/1.1 {d} OK
            \\Content-Type: text/html; charset=utf-8
            \\Content-Length: {d}
            \\Connection: close
            \\
            \\{s}
        , .{ self.status, self.body.len, self.body });
    }
};

const html_page =
    \\<!DOCTYPE html>
    \\<html>
    \\<head><title>Zig Server</title></head>
    \\<body>
    \\<h1>Hello from Zig!</h1>
    \\<p>Server ini ditulis dalam bahasa Zig.</p>
    \\</body>
    \\</html>
;

fn handleClient(client: std.net.StreamServer.Connection) void {
    defer client.stream.close();

    var buf: [1024]u8 = undefined;
    const n = client.stream.read(&buf) catch return;
    const request = buf[0..n];

    std.debug.print("Request: {s}\n", .{request[0..@min(n, 100)]});

    const response = Response{
        .status = 200,
        .body = html_page,
    };

    const formatted = response.format(std.heap.page_allocator) catch return;
    _ = client.stream.write(formatted) catch return;
}

pub fn main() !void {
    const address = try std.net.Address.parseIp("127.0.0.1", 8080);
    var server = try address.listen(.{
        .reuse_address = true,
    });
    defer server.deinit();

    std.debug.print("Server berjalan di http://127.0.0.1:8080\n", .{});

    while (true) {
        const client = try server.accept();
        // Handle each connection (single-threaded for simplicity)
        handleClient(client);
    }
}

Testing

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

fn fibonacci(n: u32) u32 {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

test "fibonacci basic cases" {
    try expectEqual(fibonacci(0), 0);
    try expectEqual(fibonacci(1), 1);
    try expectEqual(fibonacci(2), 1);
    try expectEqual(fibonacci(10), 55);
    try expectEqual(fibonacci(20), 6765);
}

test "array operations" {
    var list = std.ArrayList(i32).init(std.testing.allocator);
    defer list.deinit();

    try list.append(1);
    try list.append(2);
    try list.append(3);

    try expectEqual(list.items.len, 3);
    try expectEqual(list.items[0], 1);
    try expectEqual(list.items[2], 3);
}

🧠 Kuis: Zig Programming Language

Uji pemahaman Anda tentang Zig dengan 5 pertanyaan berikut:

1. Apa yang dimaksud dengan comptime di Zig?

  • Kompiler untuk mengcompile kode ke bytecode
  • Ekspresi yang dievaluasi saat compile time
  • Library untuk kompresi data
  • Sistem manajemen dependency

2. Apa fungsi dari 'defer' di Zig?

  • Menjadwalkan eksekusi saat scope keluar
  • Menunda kompilasi kode
  • Mendeferensiasi pointer
  • Mendefinisikan fungsi baru

3. Manakah yang merupakan error handling operator di Zig?

  • throw dan catch
  • try dan catch
  • raise dan rescue
  • error dan handle

4. Bagaimana cara memanggil fungsi C dari Zig?

  • Menggunakan FFI library eksternal
  • Membuat wrapper manual untuk setiap fungsi
  • Menggunakan @cImport dan @cInclude
  • Tidak bisa, Zig tidak kompatibel dengan C

5. Apa keuntungan utama menggunakan Allocator di Zig?

  • Kontrol penuh atas strategi alokasi memori
  • Tidak perlu dealokasi manual
  • Otomatis garbage collection
  • Hanya bisa menggunakan heap memory

📚 Sumber Belajar Lanjutan