Union menyimpan satu nilai pada satu waktu, dan tipe nilainya dapat berupa salah satu dari beberapa varian. Odin melacak varian yang sedang aktif pada waktu berjalan. Topik ini membahas deklarasi union dan ukuran penyimpanannya, pemberian varian melalui penugasan, pembacaan varian dengan type switch dan type assertion, keadaan nil beserta #no_nil, serta idiom label varian ketika informasi tambahan perlu disimpan bersama nilai. Setelah membaca, satu tipe yang menampung beberapa bentuk data dapat dinyatakan dengan aman tanpa menyimpan tipe secara manual.
Deklarasi union dan ukurannya#
Union dideklarasikan dengan kata kunci :: union diikuti daftar tipe varian. Varian di sini adalah tipe yang dapat ditampung union, misalnya u32, f32, dan string pada union Nilai. Odin menyebut struktur ini tagged union, yaitu union bertag, karena di samping data, union juga menyimpan label varian aktif. Label itulah yang membuat varian dapat diuji dengan aman pada waktu berjalan.
Ukuran union mengikuti varian terbesar ditambah label varian. Pengukuran berikut pada target 64-bit memperlihatkan pola tersebut.
| Tipe union | Varian terbesar | Ukuran union |
|---|---|---|
union { u8 } | 1 byte | 2 byte |
union { u32 } | 4 byte | 8 byte |
union { f64 } | 8 byte | 16 byte |
union { string } | 16 byte | 24 byte |
Untuk union { string }, varian string menempati 16 byte dan label varian menambah 8 byte, sehingga totalnya 24 byte. Angka-angka tersebut diperiksa dengan size_of untuk varian string pada program utuh di bawah ini.
Memberikan dan membaca varian#
Variabel union diberi varian melalui penugasan biasa, misalnya u = u32(42) atau u = "teks". Nilai awal union adalah nil, yaitu keadaan tanpa varian, dan perbandingan u == nil menguji keadaan tersebut. Membaca varian dilakukan dengan dua cara. Type switch berbentuk switch v in u menguji varian satu per satu dan mengikat nilai varian pada variabel v di dalam casenya. Type assertion berbentuk u.(string) mengambil varian tertentu, dengan bentuk comma-ok x, ok := u.(string) yang mengembalikan penanda keberhasilan alih-alih berhenti ketika varian tidak cocok.
package main
import "core:fmt"
Nilai :: union {
u32,
f32,
string,
}
Pemilih :: union #no_nil {
u32,
string,
}
main :: proc() {
fmt.println("union{string}: ukuran =", size_of(Nilai), "byte")
fmt.println("string saja :", size_of(string), "byte")
// nilai awal union adalah nil
u: Nilai
fmt.println("awal bernilai nil?", u == nil)
// varian diberikan lewat penugasan
u = u32(42)
fmt.println("varian u32 :", u)
u = "teks"
fmt.println("varian string:", u)
// type switch: varian diuji pada waktu berjalan
switch v in u {
case u32:
fmt.println("varian u32, nilai =", v)
case f32:
fmt.println("varian f32, nilai =", v)
case string:
fmt.println("varian string, nilai =", v)
case:
fmt.println("union bernilai nil")
}
// type assertion bentuk aman (comma-ok)
if x, ok := u.(string); ok {
fmt.println("u memang string:", x)
}
if x, ok := u.(f32); ok {
fmt.println("u adalah f32:", x)
} else {
fmt.println("u bukan f32, x bernilai nol:", x)
}
// union #no_nil tidak memiliki keadaan nil
p: Pemilih = 7
fmt.println("p =", p, "ukuran =", size_of(Pemilih))
p = "batal"
fmt.println("p =", p)
}odin run main.odin -fileunion{string}: ukuran = 24 byte
string saja : 16 byte
awal bernilai nil? true
varian u32 : 42
varian string: teks
varian string, nilai = teks
u memang string: teks
u bukan f32, x bernilai nol: 0
p = 7 ukuran = 24
p = batalKeluaran menunjukkan bahwa type switch memilih case sesuai varian aktif, yaitu string setelah penugasan terakhir. Pada assertion yang gagal, variabel hasil tetap terikat dengan nilai nol tipe yang diminta, sehingga cabang else dapat menanganinya. Bentuk assertion tanpa comma-ok seperti x := u.(string) juga sah, tetapi program berhenti dengan panic apabila varian aktif tidak cocok, sehingga bentuk comma-ok lebih aman untuk varian yang tidak pasti.
Satu case type switch juga boleh memuat beberapa tipe sekaligus, dengan penulisan case u32, f32:. Karena tipe varian tidak dapat ditentukan di dalam case gabungan tersebut, variabel yang diikat tetap berupa nilai union utuh dan perlu diuji lagi bila varian persisnya diperlukan. Case gabungan ini berguna ketika beberapa varian ditangani dengan langkah yang sama, misalnya mengelompokkan semua varian numerik.
Keadaan nil dan #no_nil#
Union secara bawaan dapat bernilai nil, dan nil ditangkap oleh case default pada type switch. Keadaan ini memudahkan penandaan nilai yang belum diisi, tetapi juga berarti lupa mengisi varian tidak terdeteksi sampai varian dibaca. Deklarasi union #no_nil menghilangkan keadaan tersebut. Union #no_nil wajib memiliki setidaknya dua varian, dan varian pertamanya menjadi nilai bawaan, seperti Pemilih yang diinisialisasi dengan u32 melalui p: Pemilih = 7. Ada pula tag #shared_nil yang menormalkan nilai nol dari semua variannya menjadi nil, berguna ketika setiap varian memiliki nilai nol yang berarti sama.
Idiom label varian#
Label internal union hanya menyimpan tipe varian aktif dan tidak dapat membawa informasi lain. Ketika satu varian perlu disertai keterangan tambahan, seperti pesan kesalahan atau catatan, pola yang lazim adalah struct berisi enum penanda, union, dan field keterangan. Enum penanda pada pola ini menyatakan makna bisnis dari nilai tersebut, sedangkan union menampung nilainya.
Kebutuhan semacam itu sering muncul pada hasil operasi yang dapat berhasil maupun gagal, misalnya pencarian data yang kembali berupa angka atau pesan bahwa data tidak ditemukan. Union menangani sisi variasi nilai, sedangkan enum penanda menyimpan konteks pembacaan yang tidak dapat ditampung oleh label internal, misalnya sumber kesalahan atau jenis laporan. Keduanya dipisahkan agar tipe union tetap sederhana dan label aplikasinya bebas berkembang.
package main
import "core:fmt"
Hasil_Ujian :: union {
int,
string,
}
// label aplikasi menyimpan informasi tambahan di luar varian union
Kartu_Hasil :: struct {
jenis: enum {
Nilai_Angka,
Pesan_Kesalahan,
},
isi: Hasil_Ujian,
catatan: string,
}
main :: proc() {
kartu := [2]Kartu_Hasil{
{jenis = .Nilai_Angka, isi = 88, catatan = "Ujian tengah semester"},
{jenis = .Pesan_Kesalahan, isi = "NIM tidak ditemukan", catatan = "Validasi data"},
}
for k in kartu {
switch k.jenis {
case .Nilai_Angka:
fmt.printf("nilai %v (%s)\n", k.isi, k.catatan)
case .Pesan_Kesalahan:
fmt.printf("pesan %v (%s)\n", k.isi, k.catatan)
}
}
}odin run label-varian.odin -filenilai 88 (Ujian tengah semester)
pesan NIM tidak ditemukan (Validasi data)Struct Kartu_Hasil menyimpan union Hasil_Ujian bersama enum penanda dan catatan. switch pada field jenis menentukan cara membaca field isi, sehingga pembacaan varian berlandaskan makna yang dinyatakan secara eksplisit. Untuk kasus yang hanya membutuhkan pembedaan tipe, type switch pada union sudah cukup, dan pola ini ditambahkan hanya ketika keterangan tambahan memang diperlukan.
Ringkasan#
- Union menampung satu varian dari beberapa tipe, dengan label varian yang disimpan bersama data sehingga varian dapat diuji pada waktu berjalan.
- Ukuran union mengikuti varian terbesar ditambah label varian, misalnya 24 byte untuk varian
string. - Varian dibaca dengan type switch
switch v in uatau type assertion, dan bentuk comma-ok mengembalikan penanda keberhasilan. - Nilai awal union adalah
nil, sedangkan#no_nilmenghilangkan keadaan tersebut dan menjadikan varian pertama sebagai nilai bawaan. - Untuk keterangan tambahan per varian, struct berisi enum penanda, union, dan field keterangan adalah pola yang lazim.
Referensi#
- Odin Language Overview — odin-lang.org
— Dokumentasi resmi yang membahas union, type switch, serta tag
#no_nildan#shared_nil. - Package core:fmt — pkg.odin-lang.org — Dokumentasi paket pencetakan yang menampilkan nilai union pada contoh.