Enum mendefinisikan himpunan tertutup konstanta bernama yang masing-masing membawa satu nilai integer yang diketahui saat kompilasi. Topik ini membahas penomoran tag otomatis dan eksplisit, konversi antara enum dan integer, pemeriksaan switch yang exhaustif, serta method pada enum. Sebagai contoh penerapan, alur status sebuah pesanan dimodelkan dengan enum yang memiliki method sendiri.
Konsep#
Konstanta bernama dengan tag integer#
Tipe enum adalah daftar anggota bernama, dan setiap anggota membawa satu nilai integer yang disebut tag (tag). Deklarasinya ditulis dengan kata kunci enum diikuti blok anggota. Kompilator menomori anggota secara otomatis mulai dari nol bila nilai tidak ditulis, dan memilih tipe integer terkecil yang cukup untuk memuat semua nilai tag.
// tidak-dijalankan
const Arah = enum { utara, timur, selatan, barat };
// utara bernilai 0, timur 1, selatan 2, barat 3Enum menggantikan kebiasaan menandai keadaan dengan konstanta integer lepas, misalnya const DITERIMA = 0. Dengan enum, himpunan keadaan menjadi tertutup sehingga nilai di luar daftar tidak mungkin muncul dari kode yang sah. Kompilator juga mengenal seluruh anggota enum, dan pengetahuan itulah yang dipakai untuk memeriksa kelengkapan switch pada bagian berikutnya. Penamaan anggota mengikuti konvensi pengenal Zig yang sama, yaitu huruf kecil dengan pemisah garis bawah.
Nilai tag eksplisit dan tipe tag#
Tipe tag dapat ditentukan secara eksplisit dengan menulisnya di dalam tanda kurung setelah enum, dan nilai setiap tag dapat diberikan satu per satu. Bentuk enum(u8) menyatakan bahwa tag disimpan sebagai u8, sehingga ukuran enumnya satu byte.
// tidak-dijalankan
const Warna = enum(u8) { merah = 0, hijau = 1, biru = 2 };
const warna: Warna = @enumFromInt(1);
const kode: u8 = @intFromEnum(warna);
// warna bernilai .hijau dan kode bernilai 1Fungsi bawaan @intFromEnum mengambil nilai tag dari sebuah enum, sedangkan @enumFromInt bekerja pada arah sebaliknya. Konversi balik ini perlu dijaga. Nilai integer yang diketahui saat kompilasi dan tidak cocok dengan tag mana pun ditolak langsung oleh kompilator, sedangkan nilai yang baru diketahui saat program berjalan memicu panic dengan pesan invalid enum value pada mode Debug. Nilai dari luar program, misalnya data yang dibaca dari berkas, sebaiknya diperiksa terlebih dahulu sebelum dikonversi.
Nilai tag eksplisit berguna ketika angkanya harus stabil, misalnya untuk format berkas atau protokol komunikasi. Selama dua anggota tidak diberi nilai yang sama, penomorannya boleh dicampur, yaitu sebagian eksplisit dan sebagian otomatis.
switch yang exhaustif#
Pencocokan switch atas nilai enum wajib exhaustif (exhaustive), yaitu mencakup semua anggota atau menyediakan cabang _. Satu anggota yang tertinggal menghentikan kompilasi dengan error kelengkapan switch. Pemeriksaan ini membuat penambahan anggota baru langsung menunjuk seluruh switch yang perlu diperbarui.
// tidak-dijalankan
const Kode = enum(u8) { ok = 0, gagal = 1, _ };
// switch atas Kode wajib menyediakan cabang `_` untuk nilai lainAnggota _ pada akhir deklarasi mengubah enum menjadi non-exhaustive, yaitu enum yang memperbolehkan nilai di luar daftarnya. Bentuk ini dipakai ketika daftar nilai dari luar program tidak dapat ditutup sepenuhnya. Konsekuensinya mengikuti aturan yang sama, yaitu cabang _ wajib ada agar nilai tak dikenal tetap tertangani.
Untuk enum tertutup yang exhaustif, cabang _ tidak diperlukan dan sebaiknya tidak ditulis. Cabang _ pada enum tertutup meniadakan manfaat pemeriksaan kelengkapan, karena anggota baru tidak lagi menunjuk switch yang belum menanganinya. Dengan menuliskan semua anggota secara eksplisit, kompilator menjadi pihak yang mengingatkan setiap tempat yang wajib diperbarui.
Method pada enum#
Enum adalah container sebagaimana struct, sehingga enum boleh memuat method dan konstanta. Method dipanggil dari nilai enum dengan tanda titik, dan parameter pertamanya bertipe enum itu sendiri. Nama tag sebuah nilai dapat diambil dengan @tagName, yang mengembalikan nama anggota dalam bentuk string, sedangkan penentu format {t} mencetak nama yang sama langsung ke keluaran.
Nilai enum ditulis secara ringkas dengan enum literal, yaitu tanda titik diikuti nama anggota seperti .diproses. Bentuk ini sah ketika tipe yang dituju sudah jelas dari konteksnya, misalnya pada deklarasi bertipe atau pada argumen fungsi. Kombinasi enum literal dan method membuat kode berstatus terbaca hampir seperti kalimat, tanpa konstanta integer lepas dan tanpa perbandingan string.
Method pada enum paling cocok untuk perilaku yang ditentukan sepenuhnya oleh keadaan, misalnya pesan tampilan atau pemeriksaan keadaan akhir. Perilaku yang membutuhkan data tambahan tidak ditempatkan di sini, melainkan pada union bertag yang membawa payload.
Contoh Lengkap#
Program utuh: status pesanan#
Program berikut memodelkan alur pesanan dengan tiga keadaan. Enum StatusPesanan membawa tag u8 dengan nilai eksplisit, memiliki method label yang mengembalikan keterangan berdasarkan switch exhaustif, dan fungsi main menampilkan keterangan method, nama tag, serta nilai tag numeriknya.
const std = @import("std");
const StatusPesanan = enum(u8) {
diterima = 0,
diproses = 1,
selesai = 2,
fn label(self: StatusPesanan) []const u8 {
return switch (self) {
.diterima => "baru diterima",
.diproses => "sedang diproses",
.selesai => "selesai",
};
}
};
pub fn main() void {
const status: StatusPesanan = .diproses;
std.debug.print("status: {s}\n", .{status.label()});
std.debug.print("tag : {t}\n", .{status});
std.debug.print("kode : {d}\n", .{@intFromEnum(status)});
}status: sedang diproses
tag : diproses
kode : 1Method label memakai switch tanpa cabang _, sehingga bila suatu saat anggota baru ditambahkan, kompilator langsung menunjuk method ini sebagai tempat yang belum menanganinya. Baris kedua keluaran berasal dari penentu format {t} yang mencetak nama tag, sedangkan baris ketiga berasal dari @intFromEnum yang menampilkan angka 1 sesuai nilai eksplisit .diproses.
# jalankan program dari direktori topik
zig run status-pesanan.zigstatus: sedang diproses
tag : diproses
kode : 1Kesalahan umum#
Kesalahan pada enum relatif jarang lolos karena pemeriksaan kompilatornya ketat. Dua kasus berikut tetap perlu dikenali agar pesan errornya cepat terbaca, dan satu kebiasaan buruk perlu dihindari sejak awal.
- Switch atas enum tertutup kekurangan cabang. Kompilasi berhenti dan kompilator menyebut kemungkinan yang belum ditangani. Lengkapi cabang yang hilang, atau hapus cabang
_yang menutupi kelalaian tersebut apabila sebelumnya ditulis. @enumFromIntdiberi nilai yang bukan tag mana pun. Nilai yang diketahui saat kompilasi ditolak dengan error, sedangkan nilai yang baru terbaca saat program berjalan memicu panic pada mode Debug. Validasi nilai dari luar program terlebih dahulu, misalnya dengan membandingkannya terhadap daftar nilai yang sah, sebelum konversi dilakukan.- Penomoran otomatis diandalkan untuk data eksternal. Nomor otomatis bergeser ketika anggota baru disisipkan di tengah daftar, sehingga angka yang tersimpan pada berkas lama dapat salah makna. Beri nilai tag eksplisit setiap kali angkanya berhadapan dengan format data di luar program.
Ringkasan#
- Enum adalah himpunan tertutup konstanta bernama yang membawa nilai integer, yang dapat dinomori otomatis atau diberi nilai eksplisit melalui
enum(u8). @intFromEnumdan@enumFromIntmengonversi dua arah, dengan konversi balik yang wajib dijaga terhadap nilai yang bukan tag.- switch atas enum wajib exhaustif, sedangkan anggota
_mengubah enum menjadi non-exhaustive yang menuntut cabang_. - Enum adalah container sehingga boleh memuat method dan konstanta, dan penentu format
{t}mencetak nama tag ke keluaran.
Referensi#
- enum — Zig Language Reference — Deklarasi enum, tipe tag, dan method pada enum.
- Non-exhaustive-enum — Zig Language Reference
— Enum dengan anggota
_untuk nilai di luar daftarnya.