A.28. Module System ➜ Visibility & Privacy
Kita sebenarnya sudah mempelajari banyak hal yang berhubungan dengan visibility & privacy pada beberapa chapter sebelumnya. Jika pembaca mempelajari ebook ini secara urut, maka pastinya sudah familiar dengan keyword pub, self, crate, dan super.
Chapter ini merupakan pembahasan tambahan untuk ke-4 keyword tersebut, dan fokusnya lebih ke visibility & privacy di Rust secara general.
O iya, perihal visibility dan privacy itu sendiri, kedua istilah tersebut di sini kita maknai sama, yang artinya kurang lebih adalah tentang manajemen akses item di Rust.
A.28.1. Pembahasan module system
Pastikan sudah mempelajari 5 buah chapter tentang module system yang sebelumnya sudah dibahas. Kesemua chapter tersebut sangat berhubungan dengan pembahasan chapter ini.
- A.18. Module System ➜ Path & Item
- A.19. Module System ➜ Package & Crate
- A.20. Module System ➜ Module
- A.21. Module System ➜ Inline Module
- A.22. Module System ➜ Scope & Akses Item
A.28.2. Default visibility
Di Rust, by default, hampir semua item adalah private. Apa efeknya ketika item adalah private atau publik? Silakan ingat 2 aturan penting berikut:
- Jika suatu item adalah private, maka item tersebut hanya bisa diakses dari current module scope dan dari submodules milik current module.
- Jika suatu item adalah publik, maka dia bisa diakses dari module lain di luar current module scope, dengan catatan parent module scope item tersebut harus publik.
Kita sepakati di sini, pada istilah current module kata module di situ bisa saja tertuju untuk module atau juga submodule
Dua point di atas sangat penting untuk dipahami, karena digunakan sebagai landasan pertimbangan dalam penyusunan hierarki module. Sebagai contoh, kita bisa membuat program yang hanya meng-expose API tertentu (yang memang diperlukan untuk diakses oleh publik), tanpa perlu ikut meng-expose detail implementasinya.
Ok, sekarang silakan perhatikan path sederhana di bawah ini. Diasumsikan ada sebuah fungsi yang path aksesnya adalah berikut:
messaging::service_layer::some_black_magic
Segmen pertama yaitu messaging pasti adalah publik, karena di-import ke crate root. Lalu bagaimana dengan segmen service_layer dan juga some_black_magic?
Jika item some_black_magic di situ adalah publik, maka idealnya pengaksesan menggunakan path tersebut memungkinkan. Tapi kembali ke point ke-2 aturan yang sudah dibahas di atas, yaitu meskipun some_black_magic adalah publik, jika parent-nya (yang pada konteks ini adalah service_layer) adalah private, maka pengaksesan menggunakan path tersebut menghasilkan error.
Intinya, sebuah item bisa diakses jika item tersebut adalah publik, dan parent item tersebut juga publik. Sedangkan default visibility untuk hampir semua item adalah private.
Ok, sekarang mari lanjut ke praktik menggunakan contoh dengan pembahasan yang lebih mendetail. Silakan perhatikan dan praktikkan kode berikut:
my_package
│─── Cargo.toml
└─── src
│─── messaging.rs
└─── main.rs
const SOME_MESSAGE: &str = "hello rust";
mod service_layer {
pub fn some_black_magic() {
println!("{}", crate::messaging::SOME_MESSAGE)
}
}
pub fn say_hello() {
service_layer::some_black_magic();
}
mod messaging;
fn main() {
messaging::say_hello();
}
Konstanta
messaging::SOME_MESSAGEadalah private. Penjelasan:- Konstanta ini merupakan module item milik
messaging. - Konstanta ini bisa diakses dari current module scope (
messaging). - Konstanta ini bisa diakses dari submodule milik current module, yaitu submodule dari
messaging. Contohnya bisa dilihat pada fungsimessaging::service_layer::some_black_magicyang di situ ada statement pemanggilanSOME_MESSAGE. - Konstanta ini tidak bisa diakses dari luar current module scope (
messaging).
- Konstanta ini merupakan module item milik
Submodule
messaging::service_layeradalah private. Penjelasan:- Submodule ini merupakan module item milik
messaging. - Submodule ini bisa diakses dari current module scope (
messaging). Contohnya bisa dilihat pada fungsimessaging::say_helloyang di situ ada statement pemanggilanservice_layer. - Submodule ini bisa diakses dari submodule milik current module, yaitu submodule dari
messaging. - Submodule ini tidak bisa diakses dari luar current module scope (
messaging).
- Submodule ini merupakan module item milik
Fungsi
messaging::service_layer::some_black_magicadalah publik. Penjelasan:- Fungsi ini merupakan module item milik
messaging::service_layer. - Fungsi ini bisa diakses dari current module scope (
messaging::service_layer). - Fungsi ini bisa diakses dari submodule milik current module, yaitu submodule dari
messaging::service_layer. Contohnya bisa dilihat pada fungsimessaging::say_helloyang di situ ada statement pemanggilan fungsisome_black_magic. - Fungsi ini bisa diakses dari luar current module scope (
messaging::service_layer). - Namun meskipun demikian, bisa tidaknya fungsi ini diakses dari luar current module scope (
messaging::service_layer) juga tergantung dengan visibility dari current module itu sendiri, yaitumessaging::service_layer. - Karena module
messaging::service_layeradalah private, meskipun fungsisome_black_magicdi dalamnya adalah publik, pengaksesan fungsi tersebut dari luar module scopemessaging::service_layertidak dimungkinkan.- Pengaksesan
service_layer::some_black_magicdarimessaging::say_hellotidak error karena submoduleservice_layermeskipun private, posisinya adalah masih dalam satu module scope yang sama dengan fungsisay_hello. - Dimisalkan jika
service_layer::some_black_magicdipaksa diakses darimain, maka muncul error karenaservice_layeradalah private dan posisinya tidak berada dalam module scope yang sama dengan crate root (main).
- Pengaksesan
- Fungsi ini merupakan module item milik
Fungsi
messaging::say_helloadalah public. Penjelasan:- Fungsi ini merupakan module item milik
messaging. - Fungsi ini bisa diakses dari current module scope (
messaging). - Fungsi ini bisa diakses dari submodule milik current module, yaitu submodule dari
messaging. - Fungsi ini bisa diakses dari luar current module scope (
messaging). Contohnya bisa dilihat pada crate root fungsimain, di situ ada pemanggilan statementsay_hello.
- Fungsi ini merupakan module item milik
A.28.3. Re-export item
Pada contoh, fungsi messaging::say_hello didesain sebagai media untuk mengakses fungsi some_black_magic. Di situasi real world pastinya sangat jarang terjadi sebuah fungsi isinya hanya satu baris pemanggilan fungsi lainnya. Jika memang ada situasi seperti itu, (kontekstual) lebih baik hapus saja fungsi yang jadi media pemanggilan dan langsung saja panggil fungsi di dalamnya sesuai kebutuhan.
Pada praktik selanjutnya ini kita misalkan bahwa fungsi say_hello isinya memang hanya 1 baris, dan yang paling penting adalah isi fungsi some_black_magic perlu untuk bisa diakses dari main. Untuk kasus seperti ini ada 3 alternatif solusi:
- Tidak perlu mengubah apapun, gunakan saja kode yang sudah ditulis di atas. Kode tersebut sudah bisa mengakomodir pemanggilan
some_black_magicviasay_hello. - Atau, hapus saja fungsi
say_hello, lalu ubah visibility moduleservice_layermenjadi publik, dengan demikian kita bisa mengaksessome_black_magicdarimainmenggunakan pathmessaging::service_layer::some_black_magic. - Atau, gunakan teknik re-export item.
Re-export item adalah sebuah cara untuk mem-bypass pengaksesan item yang secara hierarki memang tidak bisa diakses dari luar module (bisa jadi karena visibility item ataupun parent module nya adalah private). Dengan teknik ini, maka item pasti bisa diakses dari luar module.
Item yang di-re-export akan menjadi item milik current module di mana statement re-export tersebut ditulis.
Cara re-export item adalah menggunakan keyword pub use kemudian diikuti dengan path yang ingin di-export dan juga nama export item dengan notasi penulisan berikut:
pub use the_path as exported_name;
pub use self::service_layer::some_black_magic as say_hello;
Contoh jika diterapkan pada program yang sebelumnya sudah ditulis:
pub use self::service_layer::some_black_magic as say_hello;
const SOME_MESSAGE: &str = "hello rust";
mod service_layer {
pub fn some_black_magic() {
println!("{}", crate::messaging::SOME_MESSAGE)
}
}
mod messaging;
fn main() {
messaging::say_hello();
}
Bisa dilihat di contoh di atas, fungsi say_hello dihapus, kemudian item service_layer::some_black_magic di-re-export dengan nama say_hello. Dengannya kita bisa mengakses some_black_magic dari luar module messaging menggunakan path messaging::say_hello.
Jika item ingin di-re-export tanpa perubahan nama item, bisa gunakan notasi berikut:
pub use the_path;
pub use self::service_layer::some_black_magic;
Jika diterapkan pada program sebelumnya, kurang lebih seperti ini:
pub use self::service_layer::some_black_magic;
const SOME_MESSAGE: &str = "hello rust";
mod service_layer {
pub fn some_black_magic() {
println!("{}", crate::messaging::SOME_MESSAGE)
}
}
mod messaging;
fn main() {
messaging::some_black_magic();
}
Lebih jelasnya mengenai keyword
usedibahas pada chapter Module System ➜ Use
A.28.4. Public visibility scope
Keyword pub digunakan untuk mengubah visibility item menjadi publik. Keyword ini bisa dikombinasikan dengan salah satu dari keyword self, crate, dan super; denganya kita bisa menentukan visibility sebuah publik item dengan scope yang lebih spesifik.
◉ Keyword pub
Penulis rasa untuk penerapan keyword pub ini sudah sangat jelas, kita beberapa kali mempraktikkannya.
Dengan keyword pub, sebuah item visibility-nya menjadi publik.
◉ Keyword pub(in path)
Keyword ini menjadikan visibility item hanya di dalam path yang ditulis di pub(in path), dengan ketentuan path tersebut merupakan parent dari module item di mana keyword digunakan.
Contohnya bisa dilihat pada kode berikut. Fungsi say_hello didefinisikan publik dengan scope path ditentukan secara eksplisit adalah crate::outer_mod. Dengan demikian fungsi say_hello hanya bisa diakses dari dalam outer_mod.
Bisa dilihat di contoh, fungsi say_hello diakses dari do_something. Silakan coba saja paksa untuk mengaksesnya dari fungsi main, hasilnya pasti error.
pub mod outer_mod {
pub mod inner_mod {
// fungsi say_hello berikut hanya bisa diakses dari dalam `outer_mod`.
// pengaksesannya dari luar `outer_mod` menghasilkan error.
pub(in crate::outer_mod) fn say_hello() {
println!("hello rust")
}
}
pub fn do_something() {
inner_mod::say_hello();
}
}
fn main() {
outer_mod::do_something();
}
◉ Keyword pub(crate)
Keyword pub(crate) digunakan untuk membuat visibility item menjadi publik dengan scope akses current crate. Dengan ini item bisa diakses dari manapun asalakan masih dalam crate yang sama.
Contoh penerapannya bisa dilihat berikut ini. Fungsi say_hello visibility scope nya ditentukan adalah current crate. Fungsi tersebut bisa diakses dari outer_mod_one::do_something, dari outer_mod_two::do_something, dan juga dari fungsi main.
pub mod outer_mod_one {
pub mod inner_mod {
// fungsi ini visibility scope-nya di level crate
pub(crate) fn say_hello() {
println!("hello rust")
}
}
pub fn do_something() {
inner_mod::say_hello();
}
}
pub mod outer_mod_two {
pub fn do_something() {
crate::outer_mod_one::inner_mod::say_hello();
}
}
fn main() {
outer_mod_one::inner_mod::say_hello();
outer_mod_one::do_something();
outer_mod_two::do_something();
}
◉ Keyword pub(super)
Keyword pub(super) digunakan untuk membuat visibility item menjadi publik dengan scope akses parent module.
Pada contoh berikut, fungsi say_hello visibility scope nya ditentukan adalah parent module, artinya fungsi tersebut hanya bisa diakses dari dalam parent module (yang pada konteks ini adalah outer_mod).
pub mod outer_mod {
pub mod inner_mod {
// fungsi ini visibility scope-nya di parent module scope,
// yaitu `outer_mod`
pub(super) fn say_hello() {
println!("hello rust")
}
}
pub fn do_something() {
inner_mod::say_hello();
}
}
fn main() {
outer_mod::do_something();
}
◉ Keyword pub(self)
Keyword ini digunakan untuk membuat visibility item menjadi publik dengan scope akses hanya pada current module. Contohnya bisa dilihat pada kode program berikut.
Fungsi say_hello visibility scope-nya adalah current module. Fungsi tersebut hanya bisa diakses dari tempat yang merupakan module item dari current module yaitu inner_mod.
pub mod outer_mod {
pub mod inner_mod {
// fungsi ini visibility scope-nya di current module scope,
// yaitu `inner_mod`
pub(self) fn say_hello() {
println!("hello rust")
}
pub fn do_something() {
say_hello();
}
}
}
fn main() {
outer_mod::inner_mod::do_something();
}
Catatan chapter 📑
◉ Source code praktik
github.com/novalagung/dasarpemrogramanrust-example/../visibility_privacy