Mendefinisikan Modul untuk Mengontrol Scope dan Privasi
Di bagian ini, kita bakal bahas modul dan bagian lain dari sistem modul, yaitu
paths (jalur), yang ngebolehin kita buat namain item; keyword use yang bawa
sebuah path ke dalem scope; dan keyword pub buat bikin item jadi public.
Kita juga bakal bahas keyword as, external packages (package eksternal), dan
operator glob.
Contekan (Cheat Sheet) Modul
Sebelum kita masuk ke detail soal modul dan paths, di sini kita nyediain
referensi cepet soal gimana cara kerja modul, paths, keyword use, sama
keyword pub di compiler, dan gimana kebanyakan developer ngatur kode
mereka. Kita bakal ngebahas contoh-contoh dari tiap aturan ini di sepanjang bab
ini, tapi tempat ini cocok sekali buat dijadiin pengingat soal gimana modul itu
jalan.
- Mulai dari crate root: Pas nge-compile sebuah crate, compiler pertama kali nyari di file crate root (biasanya src/lib.rs buat library crate atau src/main.rs buat binary crate) buat nyari kode yang mau di-compile.
- Mendeklarasikan modul: Di file crate root, kita bisa mendeklarasikan
modul baru; katakanlah kita mendeklarasikan modul “garden” pake
mod garden;. Compiler bakal nyari kode modul itu di tempat-tempat ini:- Inline, di dalem kurung kurawal yang nggantiin titik koma setelah
mod garden - Di file src/garden.rs
- Di file src/garden/mod.rs
- Inline, di dalem kurung kurawal yang nggantiin titik koma setelah
- Mendeklarasikan submodul: Di file mana pun selain crate root, kita bisa
mendeklarasikan submodul. Misalnya, kita mungkin mendeklarasikan
mod vegetables;di src/garden.rs. Compiler bakal nyari kode submodul itu di dalem direktori yang namanya sama kayak modul induk (parent)-nya di tempat-tempat ini:- Inline, langsung setelah
mod vegetables, di dalem kurung kurawal bukannya titik koma - Di file src/garden/vegetables.rs
- Di file src/garden/vegetables/mod.rs
- Inline, langsung setelah
- Paths ke kode di modul: Begitu sebuah modul jadi bagian dari crate kita,
kita bisa ngerujuk ke kode di modul itu dari mana pun di crate yang sama,
selama aturan privasinya ngebolehin, pake path ke kodenya. Misalnya, tipe
Asparagusdi modul vegetables dari garden bakal ditemuin dicrate::garden::vegetables::Asparagus. - Private vs. public: Kode di dalem sebuah modul itu private (privat)
dari modul induknya secara default. Buat bikin modul jadi public (publik),
deklarasikan pake
pub modbukannyamod. Buat bikin item di dalem modul public ikutan jadi public juga, pakepubsebelum deklarasinya. - Keyword
use: Di dalem sebuah scope, keywordusebikin shortcut (jalan pintas) ke item buat ngurangin pengulangan paths yang panjang. Di scope mana pun yang bisa ngerujuk kecrate::garden::vegetables::Asparagus, kita bisa bikin shortcut pakeuse crate::garden::vegetables::Asparagus;dan dari situ kita cuma perlu nulisAsparagusbuat pake tipe itu di scope tersebut.
Di sini, kita bikin sebuah binary crate namanya backyard yang nunjukin
aturan-aturan ini. Direktori crate-nya, yang juga namanya backyard, punya file
dan direktori ini:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
File crate root di kasus ini adalah src/main.rs, dan isinya:
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {plant:?}!");
}
Baris pub mod garden; ngasih tau compiler buat masukin kode yang dia nemu
di src/garden.rs, yaitu:
pub mod vegetables;
Di sini, pub mod vegetables; artinya kode di src/garden/vegetables.rs juga
dimasukin. Kode itu adalah:
#[derive(Debug)]
pub struct Asparagus {}
Sekarang yuk kita masuk ke detail dari aturan-aturan ini dan demonstrasikan pas lagi dipake!
Ngelempokin Kode yang Terkait di Modul
Modul ngebolehin kita ngatur kode di dalem sebuah crate buat readability (keterbacaan) dan biar gampang dipake ulang (reuse). Modul juga ngebolehin kita ngontrol privasi dari item karena kode di dalem sebuah modul itu private secara default. Item private adalah detail implementasi internal yang nggak tersedia buat dipake dari luar. Kita bisa milih buat bikin modul dan item di dalemnya jadi public, yang nge-ekspos mereka biar kode eksternal bisa pake dan bergantung pada mereka.
Sebagai contoh, yuk kita tulis sebuah library crate yang nyediain fungsionalitas dari sebuah restoran. Kita bakal mendefinisikan signature dari fungsi-fungsinya tapi ngebiarin body-nya kosong buat fokus ke organisasi kodenya bukannya implementasi dari restorannya.
Di industri restoran, beberapa bagian dari restoran disebut front of house (bagian depan) dan yang lainnya back of house (bagian dapur). Front of house itu tempat para pelanggan berada; ini nyakup tempat para host ngarahin pelanggan ke tempat duduk, pelayan nerima pesanan dan pembayaran, dan bartender bikin minuman. Back of house itu tempat para koki dan tukang masak kerja di dapur, pencuci piring bersih-bersih, dan manajer ngelakuin kerjaan administratif.
Buat menstruktur crate kita pake cara ini, kita bisa ngatur fungsi-fungsinya ke
dalem modul yang bersarang. Bikin library baru namanya restaurant dengan
jalanin cargo new restaurant --lib. Terus masukin kode di Listing 7-1 ke
src/lib.rs buat mendefinisikan beberapa modul dan signature fungsi; kode ini
adalah bagian front of house.
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
front_of_house yang nyimpen modul lain yang terus nyimpen fungsiKita mendefinisikan sebuah modul pake keyword mod diikuti sama nama modulnya
(di kasus ini, front_of_house). Body dari modulnya ditaruh di dalem kurung
kurawal. Di dalem modul, kita bisa naruh modul lain, kayak di kasus ini pake
modul hosting sama serving. Modul juga bisa nampung definisi buat item lain,
kayak struct, enum, konstanta, trait, dan kayak di Listing 7-1, fungsi.
Dengan pake modul, kita bisa ngelempokin definisi yang terkait dan ngasih nama kenapa mereka terkait. Programmer yang pake kode ini bisa navigasi kodenya berdasarkan kelompok-kelompoknya bukannya harus baca semua definisinya satu-satu, bikin lebih gampang buat nemuin definisi yang relevan buat mereka. Programmer yang nambahin fungsionalitas baru ke kode ini bakal tau ke mana harus naruh kodenya biar programnya tetep teratur.
Tadi, kita sempet nyebut kalau src/main.rs sama src/lib.rs itu disebut
crate roots. Alasan dinamain gitu karena isi dari salah satu dari dua file
ini ngebentuk modul namanya crate di root dari struktur modul crate itu,
yang dikenal sebagai module tree (pohon modul).
Listing 7-2 nunjukin pohon modul buat struktur di Listing 7-1.
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
Pohon ini nunjukin gimana beberapa modul bersarang di dalem modul lain;
misalnya, hosting bersarang di dalem front_of_house. Pohonnya juga nunjukin
kalau beberapa modul itu saling sodaraan (siblings), artinya mereka
didefinisikan di modul yang sama; hosting sama serving itu sodaraan yang
didefinisikan di dalem front_of_house. Kalau modul A ada di dalem modul B,
kita bilang kalau modul A itu anaknya (child) modul B dan modul B itu induknya
(parent) modul A. Perhatiin ya kalau seluruh pohon modul itu berakar di bawah
modul implisit yang namanya crate.
Pohon modul mungkin ngingetin kita sama pohon direktori sistem file di komputer kita; ini perbandingan yang pas sekali! Kayak direktori di sistem file, kita pake modul buat ngatur kode kita. Dan kayak file di dalem direktori, kita perlu cara buat nemuin modul kita.