Kita sudah dua kali menjumpai keyword pub — menempelnya di depan mod, lalu di depan fn — tanpa membedah aturan mainnya. Chapter ini mengangkat pub ke meja bedah: bagaimana Rust menentukan sebuah item terlihat atau tersembunyi, apa yang terjadi pada setiap jenis item ketika dibuka, dan mengapa struct berperilaku berbeda dari enum. Pemahaman ini menentukan kualitas antarmuka yang kita rancang: terlalu banyak item pub membuat internal crate bocor ke luar dan sulit diubah, sementara terlalu sedikit membuat pemakai crate kesulitan. Aturan visibility Rust dirancang supaya keputusan membuka akses selalu tampak jelas di kode.

Privacy dalam Rust #

Aturan dasarnya singkat namun kuat: item privat secara bawaan, dan item privat terlihat oleh module tempat ia dideklarasikan beserta seluruh descendant module itu. Dua konsekuensi penting mengikuti aturan ini. Pertama, anak selalu bisa memakai barang milik induk — module back_of_house bebas memanggil fungsi privat di crate root. Kedua, induk tidak otomatis melihat barang milik anak — crate root tidak bisa memanggil fungsi privat milik back_of_house meskipun crate root-lah yang mendeklarasikan module itu. Keyword pub membalik situasi: item bertanda pub pada module M terlihat oleh siapa pun yang boleh melihat M sendiri, sepanjang path menuju item juga melewati module-module yang publik.

Ketika aturan ini dilanggar, kompilator menjawab dengan error E0603. Kita lihat wujud aslinya lewat dapur yang menyimpan cook_order sebagai rahasia internal:

rust
mod back_of_house {
    fn cook_order() {
        println!("Dapur memasak pesanan");
    }
}

fn main() {
    back_of_house::cook_order();
}

Pesan error kompilator:

text
error[E0603]: function `cook_order` is private
 --> main.rs:8:20
  |
8 |     back_of_house::cook_order();
  |                    ^^^^^^^^^^ private function
  |
note: the function `cook_order` is defined here
 --> main.rs:2:5
  |
2 |     fn cook_order() {
  |     ^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0603`.

Pesan error menunjuk dua lokasi sekaligus: baris pemanggilan dan baris deklarasi fungsi. Menambahkan pub pada Baris 2 (pub fn cook_order()) langsung menyelesaikan galat — satu keyword, satu izin. Pada program yang lebih besar, pembacaan E0603 perlu lebih teliti: compiler menunjuk segmen path pertama yang gagal dilalui, jadi item target bisa saja memang pub sementara module pembungkusnya masih privat — persis kasus module hosting yang kita jumpai pada Chapter Modul Dasar.

pub Bertingkat: Satu Keyword per Pintu #

Membuka satu jalur menuju item dalam berarti menandai setiap segmen jalurnya. Perjalanan crate::front_of_house::hosting::add_to_waitlist() dari main membutuhkan tiga pub: satu untuk module front_of_house, satu untuk module hosting, satu untuk fungsi add_to_waitlist. Lupakan satu saja, error E0603 muncul kembali pada segmen yang terlewat. Pola bertingkat ini bukan beban tanpa guna — setiap pub adalah keputusan yang terbaca: fungsi tertentu memang layak dipanggil, area hosting memang layak dikunjungi, dan area front_of_house memang pintu yang dibuka untuk pelanggan.

Kebalikannya berlaku untuk penyempitan: mencabut satu pub pada jalur langsung menutup seluruh cabang di bawahnya bagi pemanggil luar, tanpa perlu menyentuh item-item di ujung. Perilaku satu arah ini memudahkan audit antarmuka — meninjau pub pada tingkat module saja sudah cukup untuk tahu area mana yang terbuka, lalu pub pada item menentukan pintu mana di dalam area itu yang bisa diketuk.

struct: pub pada Tipe Tidak Membuka Field #

Jenis item yang paling sering mengejutkan pemula adalah struct. Menandai struct dengan pub hanya membuka nama tipenya — field-field di dalamnya tetap privat satu per satu, dan field privat tidak bisa dibaca maupun ditulis dari luar module pendeklarasi. Kita uji lewat menu sarapan dapur belakang yang menyimpan roti sebagai urusan publik dan buah musiman sebagai urusan internal:

rust
mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }

    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("Semangka"),
            }
        }

        pub fn buah_musiman(&self) -> &str {
            &self.seasonal_fruit
        }
    }
}

fn main() {
    let mut pesanan = back_of_house::Breakfast::summer("Roti Gandum");
    pesanan.toast = String::from("Roti gandum bakar");

    println!("Toast: {}", pesanan.toast);
    println!("Buah musiman: {}", pesanan.buah_musiman());
}

Output yang Dihasilkan:

text
Toast: Roti gandum bakar
Buah musiman: Semangka

Mari kita bedah kode di atas:

  1. Baris 2 (pub struct Breakfast): struct dibuka untuk dunia luar — namanya bisa disebut dari main. Perhatikan: keputusan ini tidak menyentuh field sama sekali.
  2. Baris 3 (pub toast: String,): satu-satunya field publik. Kode luar module boleh membaca dan mengubah field ini.
  3. Baris 4 (seasonal_fruit: String,): field privat. Hanya kode di dalam back_of_house yang boleh menyentuh field ini secara langsung.
  4. Baris 8 (pub fn summer(toast: &str) -> Breakfast): associated function publik yang berperan sebagai konstruktor. Fungsinya penting: karena field seasonal_fruit privat, kode luar tidak bisa membangun Breakfast dengan literal struct — satu-satunya jalan menciptakan nilai adalah lewat constructor milik module.
  5. Baris 15 (pub fn buah_musiman(&self) -> &str): method publik penyedia akses baca terkontrol untuk field privat. Pembacaan tetap dimungkinkan, tetapi mutasi diserahkan sepenuhnya ke kebijakan module.

Bila kita memaksakan akses langsung ke field privat — hapus method buah_musiman dari program di atas, ganti pemanggilannya menjadi println!("Buah musiman: {}", pesanan.seasonal_fruit);, dan hilangkan mut serta baris pengubahan toast — kompilasi berakhir dengan pesan E0616 yang eksplisit:

text
error[E0616]: field `seasonal_fruit` of struct `Breakfast` is private
  --> main.rs:19:42
   |
19 |     println!("Buah musiman: {}", pesanan.seasonal_fruit);
   |                                          ^^^^^^^^^^^^^^ private field

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0616`.

Kombinasi field privat plus method publik seperti di atas adalah pola encapsulation yang lazim di Rust: struct mengendalikan invarian datanya (buah musiman selalu ditentukan dapur, bukan pelanggan) sambil tetap menyediakan akses yang disengaja. Rust tidak memiliki setter khusus — method biasa menjalankan peran itu.

enum: Variant Ikut Publik Otomatis #

Enum berkebalikan dari struct: cukup enum-nya bertanda pub, seluruh variant di dalamnya ikut publik tanpa perlu ditandai satu per satu. Rancangan ini sengaja — variant enum tidak bisa dipisahkan dari tipenya. Enum dengan variant privat tidak bermakna bagi pemakai, karena match pemakai wajib mengenal seluruh variant agar tetap exhaustive. Kita lihat perbedaannya:

rust
mod menu {
    pub enum Porsi {
        Kecil,
        Sedang,
        Besar,
    }

    pub fn harga(porsi: &Porsi) -> u32 {
        match porsi {
            Porsi::Kecil => 15000,
            Porsi::Sedang => 25000,
            Porsi::Besar => 35000,
        }
    }
}

fn main() {
    let daftar = [menu::Porsi::Kecil, menu::Porsi::Sedang, menu::Porsi::Besar];
    let nama = ["Kecil", "Sedang", "Besar"];

    for i in 0..3 {
        println!("Harga porsi {}: Rp{}", nama[i], menu::harga(&daftar[i]));
    }
}

Output yang Dihasilkan:

text
Harga porsi Kecil: Rp15000
Harga porsi Sedang: Rp25000
Harga porsi Besar: Rp35000

Bedah singkat kode di atas:

  1. Baris 2 (pub enum Porsi): satu pub membuka enum beserta ketiga variant-nya. Porsi::Sedang sah dipakai langsung dari main tanpa penanda tambahan.
  2. Baris 9 (match porsi {): pencocokan pattern terhadap seluruh variant dilakukan di dalam module menu — varian enum tetap satu keluarga yang tidak terpisahkan.

Ringkasan #

Visibility Rust bertumpu pada satu aturan bawaan — semua item privat, terlihat hanya oleh module pendeklarasi dan turunannya — serta keyword pub yang membuka jalur per segmen. Galat akses dijawab error E0603 untuk item dan module, dan E0616 untuk field struct. Struct menuntut pub per field sehingga cocok untuk encapsulation, sedangkan enum membuka seluruh variant sekaligus demi kelengkapan match. Dengan hak akses berjenjang ini, kita sudah bisa menyembunyikan struktur internal — namun pemanggil luar tetap harus menyusuri cabang-cabang yang kita buka. Chapter berikutnya memperkenalkan pub use, teknik menyusun ulang jalur publik agar antarmuka crate tampak rapi.