Namespace dan metode

Mengorganisasi kode dengan deklarasi container dan memahami desugaring pemanggilan titik.

Daftar isi

Setiap berkas Zig adalah container, yaitu lingkup bernama yang menyimpan deklarasi, dan di dalam container itulah struct memperoleh fungsinya. Topik ini membahas cara mengorganisasi deklarasi di dalam container, penanda pub untuk deklarasi publik, fungsi di dalam struct yang disebut metode, serta mekanisme desugaring di balik pemanggilan titik. Sebagai contoh penerapan, sebuah struct Penghitung dibangun lengkap dengan metode yang mengubah keadaannya melalui pointer.

Konsep#

Berkas Zig sebagai container#

Istilah container mencakup setiap berkas sumber Zig beserta tipe-tipe bentukan di dalamnya, misalnya struct, enum, dan union. Semua deklarasi tingkat atas, seperti const dan fungsi, menjadi anggota container tersebut. Ketika program dijalankan dengan zig run, berkas yang diberikan menjadi root container, sedangkan @import("std") mengembalikan container pustaka standar yang dapat dirujuk seperti struct biasa.

Rujukan bertingkat dengan tanda titik adalah cara membaca struktur container. Pada std.debug.print, nama std adalah container, debug adalah container di dalamnya, dan print adalah fungsi yang dipanggil. Struktur yang sama berlaku untuk container buatan sendiri.

zig Contoh: rujukan bertingkat antar container
// tidak-dijalankan
const std = @import("std");
// std.debug.print berarti: fungsi print di dalam container debug,
// yang berada di dalam container std

Deklarasi di dalam container bebas urutannya. Fungsi main boleh diletakkan di atas maupun di bawah deklarasi tipe yang dirujuknya, karena kompilator menyelesaikan seluruh deklarasi terlebih dahulu sebelum memeriksa pemakaiannya. Dengan kebebasan ini, tipe utama lazim diletakkan di bagian atas berkas dan fungsi main ditulis paling akhir.

Menandai deklarasi publik dengan pub#

Deklarasi tanpa kata kunci pub hanya terlihat di dalam berkasnya. Berkas lain yang mengimpor container tidak dapat merujuk deklarasi tersebut, dan upaya merujuknya menghasilkan pesan error yang menyatakan bahwa simbol itu tidak ditandai pub. Kata kunci pub di depan sebuah deklarasi menjadikannya bagian dari antarmuka container.

zig Contoh: deklarasi privat dan publik
// tidak-dijalankan
// di dalam berkas util.zig
fn rahasia() u32 {
    return 1;
}

pub fn terbuka() u32 {
    return 2;
}
// util.rahasia() ditolak kompilator, sedangkan util.terbuka() sah

Aturan yang sama berlaku untuk tipe. Struct yang akan dipakai berkas lain ditulis pub const Penghitung = struct { ... }. Perlu dicatat bahwa field struct tidak memiliki penanda akses, sehingga seluruh field dapat dibaca dan diubah dari luar selama structnya sendiri dapat diakses. Batasan visibilitas hanya berlaku pada deklarasi tingkat container, bukan pada field.

Fungsi di dalam struct menjadi metode#

Fungsi yang dideklarasikan di dalam struct disebut metode (method). Metode dipanggil dari sebuah nilai dengan tanda titik, dan parameter pertamanya adalah nilai struct itu sendiri, yang lazim diberi nama self. Pemanggilan p.nilai() hanyalah bentuk singkat, sedangkan bentuk lengkapnya adalah panggilan fungsi biasa pada container, yaitu Penghitung.nilai(p).

zig Contoh: desugaring pemanggilan titik
// tidak-dijalankan
// p.nilai() dibongkar menjadi:
Penghitung.nilai(p);
// p.naik() dibongkar menjadi:
Penghitung.naik(&p);

Pembongkaran bentuk singkat ini disebut desugaring, dan kompilatorlah yang melakukannya, sehingga kedua bentuk tersebut menghasilkan kode yang persis sama. Perhatikan contoh kedua pada fragmen di atas. Karena metode naik menuntut parameter bertipe pointer, alamat p diambil dengan & ketika pemanggilannya dibongkar.

Selain fungsi, struct juga boleh memuat konstanta seperti pub const BATAS: u32 = 3. Konstanta itu menjadi anggota container Penghitung dan dirujuk dengan nama lengkapnya, yaitu Penghitung.BATAS, baik dari luar maupun dari dalam metode. Mekanisme yang sama berlaku untuk enum dan union karena semua tipe bentukan Zig adalah container, sehingga semuanya boleh memuat fungsi, konstanta, bahkan tipe lain.

Penerima self berupa nilai atau pointer#

Tipe parameter pertama menentukan apa yang diterima metode. Parameter self: Penghitung menerima salinan, sehingga metode tersebut hanya dapat membaca keadaan dan tidak pernah dapat mengubah struct asalnya. Parameter self: *Penghitung menerima pointer ke struct asli, sehingga perubahan pada self langsung mengubah nilai milik pemanggil.

Kedua bentuk dipanggil dengan sintaks titik yang sama. Kompilator mengambil alamat secara otomatis saat metode menuntut pointer, sehingga kode pemanggil cukup menulis p.naik(). Namun, pemanggilan metode mutator pada pengikatan const tetap ditolak, karena alamat yang tersedia bertipe pointer konstan yang tidak boleh dipakai untuk mengubah nilai. Untuk struct berukuran besar yang hanya dibaca, parameter self: *const Penghitung menjadi jalan tengah yang memberi akses baca tanpa menyalin nilainya.

Contoh Lengkap#

Program utuh: struct Penghitung#

Program berikut merangkum seluruh materi topik. Struct Penghitung menyimpan satu field bilangan dengan nilai bawaan nol, memuat konstanta BATAS, dan memiliki tiga metode. Metode naik menambah nilai melalui pointer kecuali nilai sudah mencapai batas, metode reset mengembalikan nilai ke nol, dan metode nilai membaca keadaan melalui salinan.

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

const Penghitung = struct {
    pub const BATAS: u32 = 3;

    nilai_saat_ini: u32 = 0,

    fn naik(self: *Penghitung) void {
        if (self.nilai_saat_ini == BATAS) return;
        self.nilai_saat_ini += 1;
    }

    fn reset(self: *Penghitung) void {
        self.nilai_saat_ini = 0;
    }

    fn nilai(self: Penghitung) u32 {
        return self.nilai_saat_ini;
    }
};

pub fn main() void {
    var p = Penghitung{};
    p.naik();
    p.naik();
    p.naik();
    p.naik();
    std.debug.print("nilai = {d}\n", .{p.nilai()});
    p.reset();
    std.debug.print("nilai = {d}\n", .{p.nilai()});
    std.debug.print("batas = {d}\n", .{Penghitung.BATAS});
}
text Keluaran
nilai = 3
nilai = 0
batas = 3

Empat pemanggilan p.naik() hanya menghasilkan tiga kenaikan karena pemanggilan keempat ditolak oleh pemeriksaan BATAS di dalam metode. Pengikatan p dideklarasikan dengan var agar alamatnya dapat diteruskan ke metode mutator. Bentuk lengkap dari p.nilai() adalah Penghitung.nilai(p), yang menerima salinan berisi satu u32, sedangkan p.naik() dibongkar menjadi Penghitung.naik(&p) yang meneruskan alamat. Setelah reset dipanggil, nilai kembali ke 0, dan baris terakhir menunjukkan cara membaca konstanta dari luar struct melalui Penghitung.BATAS.

bash
# jalankan program dari direktori topik
zig run penghitung.zig
text Keluaran
nilai = 3
nilai = 0
batas = 3

Kesalahan umum#

Kesalahan pertama berupa memanggil fungsi pribadi dari berkas lain. Deklarasi tanpa pub hanya terlihat di dalam berkasnya, dan pemanggilan lintas berkas ditolak dengan pesan deklarasi tidak ditemukan. Perbaikannya berupa menandai deklarasi dengan pub, dan hal ini sekaligus menjadi cara menyaring antarmuka modul.

Kesalahan kedua berupa memilih penerima nilai untuk metode yang mengubah field. Metode dengan penerima self: Self bekerja atas salinan, sehingga perubahan field tidak pernah terbaca pemanggil. Metode yang mengubah keadaan memakai penerima self: *Self, dan pesan error penugasan pada konstanta biasanya menjadi gejala pertama kesalahan ini.

Ringkasan#

  • Berkas Zig adalah container yang menyimpan deklarasi, dan pub menentukan deklarasi mana yang terlihat dari berkas lain.
  • Fungsi di dalam struct adalah metode, dan pemanggilan titik t.metode() dibongkar menjadi panggilan fungsi biasa T.metode(t).
  • Parameter self berupa nilai menerima salinan, sedangkan self: *T menerima pointer sehingga metode dapat mengubah struct pemanggilnya.
  • Konstanta di dalam container dirujuk dengan nama lengkapnya, misalnya Penghitung.BATAS.

Referensi#