Chapter sebelumnya meninggalkan satu pertanyaan yang belum terjawab: mengapa pemanggilan front_of_house::hosting::add_to_waitlist() boleh ditulis begitu saja, sementara pemanggilan lain memerlukan awalan crate::? Jawabannya ada pada path — notasi alamat yang kita pakai untuk menunjuk item di dalam tree module. Setelah kode tumbuh menjadi banyak cabang, setiap pemanggilan antar module sebenarnya adalah keputusan menulis alamat, dan Rust menyediakan dua gaya penulisan: path absolut yang bertolak dari crate root, serta path relatif yang bertolak dari module tempat kode berdiri. Chapter ini membedah kedua gaya tersebut beserta tiga keyword pembentuknya: crate, self, dan super.

Path: Alamat Menuju Item dalam Tree #

Mari kita mulai dari aturan dasarnya. Sebuah item dalam tree module dapat ditunjuk dengan dua bentuk path. Path absolut dimulai dengan crate:: lalu merunut cabang dari akar — misalnya crate::front_of_house::hosting::add_to_waitlist() — sehingga alamat tersebut berlaku sama di bagian mana pun kode ditulis. Path relatif dimulai dari module tempat pemanggilan berlangsung: kita bisa langsung menulis nama module anak, menulis self:: untuk module saat ini, atau super:: untuk naik satu tingkat ke parent. Kompilator menuntaskan resolusi semua path saat kompilasi — tidak ada pencarian nama saat program berjalan, dan path yang salah langsung tertahan sebagai error sebelum binary terbentuk.

Kedua bentuk itu kita lihat bekerja dalam satu program restoran. Fungsi serve_order tinggal di crate root, fix_incorrect_order tinggal di module back_of_house, dan dapur yang keliru memasak harus bisa memanggil pelayan di luar module-nya:

rust
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {
            println!("Pelanggan masuk daftar tunggu");
        }
    }
}

fn serve_order() {
    println!("Pesanan disajikan ke meja pelanggan");
}

mod back_of_house {
    pub fn fix_incorrect_order() {
        self::cook_order();
        super::serve_order();
    }

    fn cook_order() {
        println!("Dapur memasak ulang pesanan");
    }
}

fn main() {
    crate::front_of_house::hosting::add_to_waitlist();
    crate::back_of_house::fix_incorrect_order();
    back_of_house::fix_incorrect_order();
}

Output yang Dihasilkan:

text
Pelanggan masuk daftar tunggu
Dapur memasak ulang pesanan
Pesanan disajikan ke meja pelanggan
Dapur memasak ulang pesanan
Pesanan disajikan ke meja pelanggan

Mari kita bedah pemanggilan-pemanggilan kunci pada kode di atas:

  1. Baris 9 (fn serve_order()): fungsi ini privat dan tinggal di crate root. Aturan privacy dari Chapter sebelumnya mengizinkan back_of_house — anak dari crate root — untuk melihatnya.
  2. Baris 14 (pub fn fix_incorrect_order()): fungsi publik yang menjadi pintu resmi module back_of_house. Isinya mendemonstrasikan dua keyword sekaligus: self::cook_order() dan super::serve_order().
  3. Baris 15 (self::cook_order();): keyword self menunjuk module saat ini, yaitu back_of_house. Penulisan ini setara dengan back_of_house::cook_order() — keduanya sah; bentuk self:: menegaskan bahwa item itu warga module kita sendiri.
  4. Baris 16 (super::serve_order();): keyword super naik satu tingkat dari back_of_house ke crate root, lalu mencari serve_order di sana. Penulisan ini setara dengan crate::serve_order().
  5. Baris 26 dan Baris 27: dua pemanggilan dari main menunjukkan pilihan gaya. Baris 26 memakai path absolut crate::back_of_house::fix_incorrect_order(), Baris 27 memakai path relatif back_of_house::fix_incorrect_order() — keduanya menunjuk fungsi yang sama, terbukti dari output yang tercetak dua kali.

Perhatikan satu hal menarik pada Baris 27: back_of_house sendiri sebenarnya module privat di crate root, tetapi main tetap boleh memanggilnya karena main berada di crate root — module tempat back_of_house dideklarasikan. Path dan visibility adalah dua aturan yang bekerja berdampingan: path menentukan alamat, privacy menentukan izin masuk di setiap cabang yang dilalui.

Salah ketik pada alamat pun ditangkap di tahap kompilasi yang sama. Menulis segmen module yang keliru — misalnya front_of_house::hosting2::add_to_waitlist() — dijawab error E0433 cannot find 'hosting2' in 'front_of_house'; salah ketik pada nama fungsi di ujung path dijawab E0425 cannot find function ... in module lengkap dengan saran nama terdekat. Kedua pesan menunjuk segmen persis yang gagal, sehingga memperbaiki path secepat membaca nomor barisnya. Sifat ini juga membuat penataan ulang module menjadi aman: memindahkan item ke cabang baru memutus seluruh pemanggil lama dengan daftar lokasi yang lengkap, bukan menunjuk item yang salah secara diam-diam.

super untuk Kode Bersarang Dalam #

Keyword super paling terasa manfaatnya ketika tree menjadi dalam. Membaca super::serve_order() dari back_of_house jelas; namun pada module bersarang dua tingkat, super tetap hanya naik satu tingkat — bukan langsung ke akar. Kita lihat pada area pemanggang di dalam dapur:

rust
mod dapur {
    fn buka_shift() {
        println!("Shift pagi dimulai, semua stasiun siap");
    }

    pub mod pemanggang {
        pub fn panggang_steak() {
            println!("Steak dipanggang medium well");
            super::buka_shift();
        }
    }
}

fn main() {
    self::dapur::pemanggang::panggang_steak();
}

Output yang Dihasilkan:

text
Steak dipanggang medium well
Shift pagi dimulai, semua stasiun siap

Bedah kode di atas:

  1. Baris 2 (fn buka_shift()): fungsi privat milik module dapur. Hanya dapur beserta turunannya yang boleh melihat fungsi ini.
  2. Baris 6 (pub mod pemanggang): module anak kedua tingkat. Seluruh item di dalamnya berjarak dua cabang dari crate root.
  3. Baris 9 (super::buka_shift();): dari dalam pemanggang, super naik satu tingkat ke dapur, lalu menemukan buka_shift. Satu super = satu tingkat; bila targetnya crate root, penulisan yang jernih adalah crate:: alih-alih menumpuk super::super:: yang rawan salah hitung.
  4. Baris 15 (self::dapur::pemanggang::panggang_steak();): dari main yang juga berdiri di crate root, self menunjuk module saat ini — yaitu crate root itu sendiri. Penulisan ini setara dengan crate::dapur::pemanggang::panggang_steak() maupun dapur::pemanggang::panggang_steak().

Kapan Memakai Path Absolut, Kapan Relatif #

Pilihan bentuk path bukan sekadar selera — komunitas Rust menjunjung pedoman yang membuat kode mudah dipindahkan. Path absolut dengan crate:: unggul ketika alamat ditujukan untuk konsumen luar atau ketika kita ingin kode tetap benar meskipun baris itu dipindahkan ke module lain, karena alamat dari akar tidak pernah berubah. Path relatif dengan super unggul untuk pemanggilan internal jarak dekat: saat refactoring mengubah nama module pembungkus, path relatif antar tetangga sering tidak perlu disentuh. Path self:: paling sering dipakai untuk menegaskan item milik module sendiri, termasuk saat nama item bertabrakan dengan nama yang dibawa masuk oleh use.

Rangkuman tiga keyword perannya dapat dipetakan dalam tabel berikut:

KeywordContoh penulisanTitik tolak
cratecrate::back_of_house::fix_incorrect_order()Akar crate — bentuk absolut
selfself::cook_order()Module tempat kode berdiri
supersuper::serve_order()Satu tingkat di atas module saat ini

Ringkasan #

Path adalah alamat item dalam tree module: bentuk absolut bertolak dari crate root lewat crate::, bentuk relatif bertolak dari module saat ini lewat nama anak langsung, self::, atau super:: untuk naik satu tingkat ke parent. Kompilator meresolusi semua path saat kompilasi, sehingga alamat salah tidak pernah lolos menjadi binary. Pedoman praktisnya pun sederhana: crate:: untuk pemanggilan lintas module, super:: untuk tetangga dekat, self:: untuk menegaskan barang milik sendiri. Path yang kita tulis masih terasa panjang dan berulang — dan di situlah keyword use masuk sebagai pembawa nama ke dalam scope, bahan Chapter berikutnya.