Kode yang Modular
Latihan membuat instruksi pembelajaran yang membahas modularitas dalam bahasa pemrograman Rust.
Modularitas adalah praktik membagi program menjadi potongan-potongan kecil yang terpisah, sehingga lebih mudah dipahami, dirawat, diuji, dan digunakan kembali. Rust menyediakan sistem modul yang kuat dan terintegrasi dengan manajemen proyek melalui cargo
. Modularitas penting bukan hanya untuk proyek besar, tetapi juga sejak awal belajar, agar pembelajar terbiasa menulis kode yang terstruktur.
Tujuan Pembelajaran
Setelah mempelajari materi ini, Anda akan mampu:
Memahami konsep modul, crate, dan pub di Rust.
Menggunakan file
lib.rs
danmain.rs
dengan tepat.Menyusun kode dalam struktur direktori modular.
Menggunakan perintah
cargo fmt
,cargo clippy
, dancargo test
untuk menjaga kualitas kode.Membedakan unit test dan integration test beserta cara menuliskannya.
Mengerti hubungan modularisasi dengan desain perangkat lunak berbasis domain.
Prasyarat
Mengetahui dasar Rust: variabel, fungsi, dan
main
.Mengetahui cara membuat proyek baru dengan
cargo new
.Familiar dengan konsep file dan direktori.
Konsep Modularitas di Rust
1. Crate
Crate adalah satuan kompilasi di Rust. Ada dua jenis crate:
Binary crate → menghasilkan program yang bisa dijalankan.
Library crate → menghasilkan pustaka yang bisa digunakan oleh crate lain.
Saat Anda membuat proyek dengan cargo new my_project
, secara default akan dibuat crate binary dengan file src/main.rs
. Jika Anda menambahkan file src/lib.rs
, proyek Anda akan memiliki crate library yang bisa digunakan di main.rs
maupun oleh proyek lain.
2. Modul (mod
)
Modul digunakan untuk mengelompokkan kode agar terorganisasi. Modul bisa berada:
Di file yang sama (
mod nama_modul { ... }
)Di file terpisah (lebih disarankan jika modul panjang).
Contoh modul di file terpisah:
src/modul_matematika.rs
pub fn jumlah(a: i32, b: i32) -> i32 {
a + b
}
pub fn maksimum(a: i32, b: i32) -> i32 {
if a > b { a } else { b }
}
src/main.rs
mod modul_matematika; // mendeklarasikan modul agar dikenal di main.rs
fn main() {
let a = 10;
let b = 20;
let hasil = modul_matematika::jumlah(a, b);
let maks = modul_matematika::maksimum(a, b);
println!("Jumlah = {}", hasil);
println!("Maksimum = {}", maks);
}
3. pub
dan Visibilitas
Secara default, fungsi, struct, atau variabel di modul bersifat private (tidak bisa diakses dari luar modul). Dengan menambahkan pub
, item tersebut menjadi public dan bisa diakses dari modul lain.
Contoh:
mod contoh {
pub fn fungsi_terbuka() {
println!("Bisa diakses dari luar modul");
}
fn fungsi_tertutup() {
println!("Hanya bisa diakses di modul ini");
}
}
fn main() {
contoh::fungsi_terbuka();
// contoh::fungsi_tertutup(); // ERROR: fungsi ini private
}
4. lib.rs
File lib.rs
berfungsi sebagai titik masuk untuk crate library.
Semua
mod
yang dideklarasikan dilib.rs
bisa dipakai oleh file lain di proyek yang sama (termasukmain.rs
).Ini memudahkan pemisahan kode besar ke dalam crate yang terpisah.
Contoh:
src/lib.rs
pub mod modul_matematika; // mempublikasikan modul
src/main.rs
use my_project::modul_matematika; // menggunakan crate library dari lib.rs
fn main() {
println!("{}", modul_matematika::jumlah(3, 4));
}
Best Practice Struktur Direktori
Rust memiliki pola standar:
my_project/
├── Cargo.toml
└── src/
├── main.rs // program utama (binary crate)
├── lib.rs // library crate (opsional)
├── modul_a.rs
└── modul_b/
├── mod.rs
├── submodul1.rs
└── submodul2.rs
└── tests/
└── integration_test.rs
Gunakan
mod.rs
jika sebuah folder berisi submodul.Pisahkan kode sesuai domain (misalnya modul
user
,order
,payment
).Pendekatan ini memudahkan penggunaan Domain-Driven Design (DDD) jika proyek menjadi lebih besar. Dalam DDD, setiap "domain" memiliki modulnya sendiri yang berisi entitas, logika, dan layanan terkait.
Menjaga Kualitas Kode
Rust menyediakan alat bantu melalui cargo
:
cargo fmt
Memformat kode sesuai gaya standar Rust.
Jalankan:
cargo fmt
Hasil: kode otomatis dirapikan (indentasi, spasi, baris kosong).
cargo clippy
Memberikan saran perbaikan kode dan mengingatkan potensi bug.
Jalankan:
cargo clippy
Contoh: akan memperingatkan jika ada kode seperti
if x == true
yang bisa disederhanakan menjadiif x
.
cargo test
Menjalankan semua unit test dan integration test di proyek.
Jalankan:
cargo test
Unit Test vs Integration Test
Unit Test → Menguji fungsi atau modul tertentu secara terisolasi. Biasanya ditulis di dalam file modul itu sendiri menggunakan blok
#[cfg(test)]
.
Contoh unit test di modul_matematika.rs
:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_jumlah() {
assert_eq!(jumlah(2, 3), 5);
}
}
Integration Test → Menguji banyak modul sekaligus untuk memastikan semuanya bekerja bersama dengan benar. Diletakkan di folder
tests/
.
Contoh file tests/integration_test.rs
:
use my_project::modul_matematika;
#[test]
fn test_integration() {
let hasil = modul_matematika::jumlah(4, 6);
assert_eq!(hasil, 10);
}
Unit test memastikan komponen kecil benar, sedangkan integration test memastikan sistem secara keseluruhan berfungsi.
Refleksi
Bagaimana modularisasi memudahkan pemeliharaan kode jangka panjang?
Apa peran
pub
dalam menjaga batas antara implementasi internal dan API yang dipublikasikan?Mengapa penting menggunakan
cargo fmt
,cargo clippy
, dancargo test
secara rutin?Bagaimana struktur direktori yang baik mempermudah kerja tim, khususnya jika menggunakan pendekatan berbasis domain (DDD)?
Bagaimana Anda membayangkan menulis unit test untuk setiap modul kecil agar mencegah bug sebelum proyek menjadi kompleks?