Pemrograman · Bahasa pemrograman
Tutorial Zig untuk pemula
Jawaban singkat
Zig adalah bahasa pemrograman sistem serbaguna sekaligus toolchain yang dimaksudkan sebagai alternatif modern untuk C. Zig tidak punya alur kontrol tersembunyi, garbage collector, maupun alokasi memori tersembunyi: Anda meneruskan allocator secara eksplisit, menangani error dengan error union dan try, dan membersihkan dengan defer. Eksekusi kode saat kompilasi (comptime) menggantikan macro dan generic. Jalankan file dengan zig run hello.zig. Zig masih di bawah versi 1.0, sehingga detail bahasa dan standard library berubah antarrilis.
Apa itu Zig dan siapa yang memakainya
Zig dibuat oleh Andrew Kelley dan dikembangkan oleh Zig Software Foundation yang nirlaba. Zig menyasar pekerjaan yang sama dengan C: komponen sistem operasi, firmware embedded, game engine, database, dan tool command-line yang cepat. Compiler Zig juga dapat mengompilasi kode C dan C++ (zig cc) dan melakukan cross-compile ke banyak target, sehingga sebagian proyek memakainya hanya sebagai toolchain C.
Proyek terkenal yang ditulis dalam Zig antara lain runtime JavaScript Bun, database keuangan TigerBeetle, dan emulator terminal Ghostty.
Memasang Zig dan menjalankan Hello, world
Unduh arsip siap pakai untuk sistem Anda dari ziglang.org/download, ekstrak, lalu tambahkan foldernya ke PATH, atau pasang Zig dengan package manager sistem Anda. Tidak ada installer atau runtime terpisah.
// hello.zig
const std = @import("std");
pub fn main() void {
std.debug.print("Hello, world!\n", .{});
}zig run hello.zig # compile and run
# Hello, world!
zig build-exe hello.zig # produce an executable named hello
zig init # create a project with build.zigstd.debug.print menulis ke standard error dan merupakan cara paling sederhana untuk mencetak di setiap versi terbaru. Argumen kedua adalah tuple berisi nilai untuk placeholder format; .{} adalah tuple kosong. API untuk menulis ke standard output berubah antarrilis, jadi periksa dokumentasi standard library versi Anda.
Variabel dan tipe dasar
const mendeklarasikan nilai yang tidak pernah berubah dan var mendeklarasikan variabel yang dapat diubah. Compiler-nya ketat: variabel lokal yang tidak dipakai adalah error, dan var yang tidak pernah diubah juga error, jadi gunakan const secara default.
| Tipe | Contoh | Catatan |
|---|---|---|
| Integer bertanda | i8, i32, i64 | Lebar berapa pun diizinkan, mis. i7 |
| Integer tak bertanda | u8, u32, usize | usize seukuran pointer, dipakai untuk panjang dan indeks |
| Float | f32, f64 | IEEE 754 |
| bool | true, false | Tidak ada konversi implisit dari integer |
| Array | [3]u8 | Panjang tetap yang diketahui saat kompilasi |
| Slice | []const u8 | Pointer ditambah panjang; string adalah slice byte |
| Optional | ?i32 | Sebuah nilai atau null |
| Error union | !u32 | Sebuah nilai atau error |
const std = @import("std");
pub fn main() void {
const age: u32 = 30;
var count: i32 = 0;
count += 1;
const name = "Ana"; // string literal, coerces to []const u8
std.debug.print("{s} is {d}, count = {d}\n", .{ name, age, count });
var maybe: ?i32 = null;
maybe = 5;
const value = maybe orelse 0;
std.debug.print("value = {d}\n", .{value});
}Placeholder format menentukan cara mencetak: {d} untuk angka, {s} untuk string, {c} untuk satu byte sebagai karakter, {any} untuk men-debug nilai apa pun.
Alur kontrol
if, switch, while, dan for semuanya dapat dipakai sebagai ekspresi. switch harus mencakup setiap nilai yang mungkin, jadi biasanya Anda menambahkan cabang else.
const std = @import("std");
pub fn main() void {
const n: i32 = -2;
const sign: []const u8 = if (n > 0) "positive" else if (n < 0) "negative" else "zero";
const score: u8 = 85;
const letter: u8 = switch (score) {
90...100 => 'A',
80...89 => 'B',
else => 'C',
};
std.debug.print("{s} {c}\n", .{ sign, letter }); // negative B
var i: u32 = 0;
while (i < 3) : (i += 1) {
std.debug.print("{d} ", .{i}); // 0 1 2
}
const items = [_]u8{ 10, 20, 30 };
for (items, 0..) |item, index| {
std.debug.print("\n{d}: {d}", .{ index, item });
}
std.debug.print("\n", .{});
}[_]u8{ ... } membiarkan compiler menghitung panjang array. for (items, 0..) melakukan perulangan atas item dan penghitung secara bersamaan; nilai di antara tanda pipa disebut capture.
Fungsi dan comptime
Fungsi mendeklarasikan tipe parameter dan tipe kembalian. Argumen bersifat immutable di dalam fungsi. Kode generik ditulis dengan parameter comptime: tipe adalah nilai yang ada saat kompilasi, sehingga sebuah fungsi dapat menerima tipe sebagai argumen.
const std = @import("std");
fn add(a: i32, b: i32) i32 {
return a + b;
}
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
pub fn main() void {
std.debug.print("{d}\n", .{add(2, 3)}); // 5
std.debug.print("{d}\n", .{max(i32, 3, 7)}); // 7
std.debug.print("{d}\n", .{max(f64, 1.5, 0.5)}); // 1.5
}Compiler membuat versi max yang terpisah untuk setiap tipe yang dipakai saat memanggilnya. Mekanisme yang sama dipakai untuk membangun struktur data generik di standard library: fungsi yang menerima tipe dan mengembalikan tipe struct baru.
Penanganan error dengan error union, try, dan defer
Error adalah nilai. Tipe kembalian !u32 berarti "sebuah u32 atau error". try expr mengembalikan error ke pemanggil bila ada; catch menanganinya di tempat. Error tidak dapat diabaikan begitu saja.
const std = @import("std");
fn parseAge(s: []const u8) !u32 {
const n = try std.fmt.parseInt(i32, s, 10);
if (n < 0) return error.Negative;
return @intCast(n);
}
pub fn main() void {
const age = parseAge("-5") catch |err| {
std.debug.print("error: {s}\n", .{@errorName(err)});
return;
};
std.debug.print("age {d}\n", .{age});
}
// error: Negativedefer menjalankan sebuah statement saat blok saat ini selesai, dan errdefer menjalankannya hanya ketika blok selesai dengan error. Keduanya menjaga kode pembersihan tetap dekat dengan kode yang memperoleh resource.
Memori dan allocator
Zig tidak punya garbage collector maupun allocator global tersembunyi. Setiap fungsi yang memerlukan memori heap menerima parameter std.mem.Allocator, sehingga pemanggil yang menentukan dari mana memori berasal (page allocator, arena, buffer tetap, atau allocator debugging yang mendeteksi kebocoran).
const std = @import("std");
pub fn main() !void {
const allocator = std.heap.page_allocator;
const buf = try allocator.alloc(u32, 5);
defer allocator.free(buf);
for (buf, 0..) |*slot, i| {
slot.* = @intCast(i * i);
}
std.debug.print("{any}\n", .{buf}); // { 0, 1, 4, 9, 16 }
}|*slot| menangkap pointer ke setiap elemen agar perulangan dapat menulis padanya, dan slot.* melakukan dereference pointer. Pada build Debug dan ReleaseSafe, integer overflow dan pengindeksan di luar batas menghentikan program dengan panic yang jelas alih-alih menyebabkan undefined behavior.
Program lengkap kecil: rata-rata angka
const std = @import("std");
fn average(numbers: []const i64) ?f64 {
if (numbers.len == 0) return null;
var sum: i64 = 0;
for (numbers) |n| sum += n;
const total: f64 = @floatFromInt(sum);
const len: f64 = @floatFromInt(numbers.len);
return total / len;
}
pub fn main() !void {
const allocator = std.heap.page_allocator;
const buf = try allocator.alloc(i64, 16);
defer allocator.free(buf);
var it = std.mem.tokenizeScalar(u8, "4 8 15 16 23 42", ' ');
var count: usize = 0;
while (it.next()) |word| : (count += 1) {
buf[count] = try std.fmt.parseInt(i64, word, 10);
}
if (average(buf[0..count])) |avg| {
std.debug.print("count={d} avg={d:.2}\n", .{ count, avg });
}
}
// count=6 avg=18.00Program ini memecah string pada spasi, mem-parse setiap potongan dengan try, menyimpan angka di memori heap yang dibebaskan oleh defer, dan mengembalikan rata-rata optional yang bernilai null untuk list kosong.
Langkah selanjutnya
- Referensi bahasa di ziglang.org/documentation, pilih halaman untuk versi yang Anda pasang.
- Bagian Learn di ziglang.org, termasuk "Getting Started" dan ikhtisar bahasa.
- Ziglings, kumpulan program rusak kecil yang Anda perbaiki satu per satu, adalah set latihan komunitas yang populer.
- Baca source standard library: ditulis dalam Zig dan merupakan referensi terbaik untuk API terkini.
FAQ
Apakah Zig siap untuk produksi?
Beberapa perusahaan memakai Zig di produksi, tetapi bahasanya masih pra-1.0, jadi Anda sebaiknya mengharapkan perubahan yang merusak saat meng-upgrade compiler dan menyediakan waktu untuk itu.
Apakah Zig pengganti C atau Rust?
Zig paling dekat dengan C: manajemen memori manual dengan pemeriksaan keamanan yang lebih baik, penanganan error, dan kode saat kompilasi. Rust menjamin keamanan memori lewat sistem ownership dan borrow checker, yang tidak dimiliki Zig.
Bisakah Zig memakai library C?
Bisa. Zig dapat meng-import file header C dan memanggil fungsi C secara langsung, dan compiler Zig dapat membangun file source C dan C++, sehingga mencampur bahasa-bahasa itu mudah dilakukan.
Apa arti comptime di Zig?
comptime menandai kode atau parameter yang dievaluasi saat kompilasi. Zig memakainya untuk generic, tabel konstan, dan pemeriksaan saat kompilasi alih-alih bahasa macro terpisah.