Kata kunci distinct membentuk tipe baru dari tipe dasar sehingga nilainya tidak dapat dicampur dengan tipe asalnya, sedangkan using menyisipkan field dari satu tipe ke dalam lingkup akses tipe lain. Topik ini membahas distinct sebagai pembungkus satuan pengukuran beserta procnya, using untuk komposisi struct, penyebaran enum ke lingkup proc, dan kesalahan umum yang menyertai keduanya. Setelah membaca, satuan seperti meter dan kilogram dapat dipisahkan dari bilangan polos, dan komposisi tipe dapat ditulis tanpa meniru warisan.
distinct: tipe baru dari tipe dasar#
Deklarasi Meter :: distinct f64 membuat tipe Meter yang tata letaknya sama dengan f64, tetapi berdiri sebagai tipe berbeda. Nilai Meter tidak dapat dijumlahkan langsung dengan f64, dan Meter tidak dapat ditukar dengan Kilogram walau keduanya berbasis f64. Konversi antartipe memerlukan cast eksplisit seperti Meter(3.5) untuk membungkus nilai atau f64(x) untuk membuka nilai kembali. Aturan ini memindahkan pemeriksaan satuan dari ingatan pengembang ke compiler, sehingga penjumlahan jarak dengan berat langsung terdeteksi sebagai error.
package main
import "core:fmt"
Meter :: distinct f64
Kilogram :: distinct f64
total_jarak :: proc(jarak: []Meter) -> Meter {
total := Meter(0)
for m in jarak {
total += m
}
return total
}
main :: proc() {
segmen := []Meter{Meter(3.5), Meter(1.25), Meter(2.0)}
fmt.println("total jarak:", total_jarak(segmen))
berat := Kilogram(2.5)
fmt.println("berat:", berat)
// operasi antarnilai bertipe sama sah dilakukan
ganda := berat * Kilogram(2)
fmt.println("berat ganda:", ganda)
// nilai dasar diperoleh lewat cast eksplisit
fmt.println("segmen pertama:", f64(segmen[0]), "meter")
fmt.println("size_of(Meter):", size_of(Meter))
}odin run main.odin -filetotal jarak: 6.75
berat: 2.5
berat ganda: 5
segmen pertama: 3.5 meter
size_of(Meter): 8Proc total_jarak hanya menerima []Meter, sehingga pemanggil dengan slice []f64 ditolak compiler. Di dalam proc, penjumlahan total += m sah karena kedua operannya bertipe Meter, dan hasilnya kembali sebagai Meter. Ukuran size_of(Meter) yang tetap 8 byte menunjukkan bahwa distinct tidak menambah pembungkus pada memori, hanya menambah aturan tipe pada waktu kompilasi.
using untuk komposisi struct#
Deklarasi using orang: Orang di dalam struct menyisipkan field milik Orang seolah-olah menjadi field milik struct tersebut. Akses k.nama dan k.umur sah tanpa menulis k.orang.nama, dan akses yang sama berlaku melalui pointer. Tata letak memori tidak berubah karena field tertanam tetap disimpan di dalam struct pembungkusnya, yang terlihat pada keluaran struct lengkap berikut.
package main
import "core:fmt"
Orang :: struct {
nama: string,
umur: u8,
}
Karyawan :: struct {
using orang: Orang,
id: u32,
divisi: string,
}
main :: proc() {
k := Karyawan{orang = Orang{nama = "Dewi", umur = 30}, id = 5001, divisi = "Analitik"}
fmt.println(k)
fmt.println("akses langsung:", k.nama, k.umur)
fmt.println("field sendiri :", k.id, k.divisi)
// lewat pointer pun aksesnya sama
p := &k
fmt.println("lewat pointer :", p.nama, p.id)
}odin run komposisi.odin -fileKaryawan{orang = Orang{nama = "Dewi", umur = 30}, id = 5001, divisi = "Analitik"}
akses langsung: Dewi 30
field sendiri : 5001 Analitik
lewat pointer : Dewi 5001Pola ini meringkas akses pada struct yang berlapis, misalnya entitas grafis yang menyimpan posisi Vector3. Tanpa using, setiap pembacaan posisi memerlukan dua tingkat nama field, sedangkan dengan using pembacaannya menjadi satu tingkat. Field tertanamnya sendiri tidak hilang, karena k.orang.nama tetap sah dan bermanfaat ketika struct Orang perlu dikirim utuh ke proc lain. Field using juga boleh lebih dari satu, sehingga struct yang menggabungkan data identitas dan alamat tetap dapat diakses dengan satu tingkat nama. Komposisi dengan using selalu diizinkan pada field struct, berbeda dengan using sebagai pernyataan pada proc yang bersifat opsional dan dibahas di bawah.
using pada enum#
Kata kunci using juga dapat menyebarkan anggota enum ke lingkup proc, sehingga anggota dapat ditulis tanpa awalan nama tipe. Kemampuan ini dinonaktifkan secara bawaan dan perlu diaktifkan per berkas dengan direktif #+feature using-stmt pada baris pertama berkas. Setelah diaktifkan, using Warna membuat Merah, Hijau, dan Biru dapat digunakan langsung di dalam proc tersebut.
#+feature using-stmt
package main
import "core:fmt"
Warna :: enum {
Merah,
Hijau,
Biru,
}
main :: proc() {
// menyebarkan anggota enum ke lingkup prosedur
using Warna
fmt.println(Merah, Hijau, Biru)
a := Hijau
fmt.println("a =", a)
}odin run sebar-enum.odin -fileMerah Hijau Biru
a = HijauDokumentasi resmi menyarankan pemilih implisit seperti .Hijau daripada menyebarkan enum dengan using, karena penyebaran menambah nama ke lingkup proc, dan nama tersebut dapat berbenturan dengan nama lain. Pemilih implisit tidak memerlukan fitur apa pun dan tetap pendek untuk ditulis, sehingga penyebaran enum sebaiknya digunakan hanya ketika anggota enum muncul sangat sering dalam satu proc.
Kesalahan umum#
Kesalahan yang paling lazim adalah menganggap using sebagai warisan. using hanya menyisipkan nama field ke lingkup akses, tanpa polimorfisme dan tanpa penimpaan proc, sedangkan field tertanam tetap tersimpan sebagai field tersendiri dan tetap terlihat pada keluaran struct. Tidak ada relasi subtipe yang terbentuk antara Karyawan dan Orang, sehingga nilai Karyawan tidak dapat digunakan di tempat yang menuntut Orang menurut aturan tipe. Ketika perilaku berbeda per tipe diperlukan, mekanisme Odin adalah proc dan polimorfisme parametrik, bukan warisan.
Kesalahan berikutnya pada sisi distinct adalah lupa melakukan cast ketika bertukar data dengan tipe dasarnya. Ekspresi jarak := Meter(3.5) + 2.0 ditolak compiler karena operan kedua bertipe f64, dan perbaikannya adalah menjelaskan maksud penulisannya, misalnya Meter(3.5) + Meter(2.0) untuk menambah jarak atau f64(Meter(3.5)) + 2.0 bila bilangan polos memang diinginkan. Terakhir, using sebagai pernyataan yang menyebarkan field pointer parameter ke lingkup proc juga dinonaktifkan secara bawaan dengan fitur using-stmt, sedangkan using pada contoh komposisi struct di atas tidak memerlukan direktif apa pun.
Perbedaan kedua kata kunci tersebut dapat dirangkum dalam tabel berikut.
| Aspek | distinct | using |
|---|---|---|
| Tujuan | tipe baru yang berdiri sendiri | akses field tertanam yang lebih pendek |
| Tata letak memori | sama dengan tipe dasarnya | field tertanam tetap tersimpan di dalam struct |
| Aturan tipe | antartipe distinct tidak boleh dicampur tanpa cast | tidak ada relasi subtipe antar struct |
| Polimorfisme | tidak ada | tidak ada |
Ringkasan#
distinctmembentuk tipe baru dari tipe dasar dengan tata letak yang sama, tetapi nilai antartipe tidak dapat dicampur tanpa cast eksplisit.- Tipe distinct cocok sebagai pembungkus satuan, dan procnya dapat menolak tipe satuan yang salah pada waktu kompilasi.
using orang: Orangmenyisipkan field struct tertanam ke lingkup akses struct pembungkus tanpa mengubah tata letak memori.usingbukan warisan, karena tidak ada polimorfisme, penimpaan proc, maupun relasi subtipe yang terbentuk.- Penyebaran enum dengan
usingmemerlukan#+feature using-stmt, sedangkan pemilih implisit seperti.Hijauadalah saran dokumentasi resmi.
Referensi#
- Odin Language Overview — odin-lang.org — Dokumentasi resmi yang membahas using dengan struct fields, using sebagai pernyataan, dan pemilih implisit.
- Package core:fmt — pkg.odin-lang.org — Dokumentasi paket pencetakan yang menampilkan nilai distinct dan struct berkomposisi pada contoh.