Topik ini membahas tata letak berkas pada proyek Cargo: perbedaan peran src/main.rs sebagai crate biner dan src/lib.rs sebagai crate pustaka, aturan pencarian berkas modul, serta penataan modul ke berkas terpisah. Setelah membacanya, pembaca dapat membangun proyek multi-berkas yang bekerja sama antara program utama dan pustakanya sendiri, serta memilih lokasi berkas yang benar untuk setiap deklarasi modul.
Dua crate dalam satu paket#
Sebuah paket Cargo dapat memuat dua crate sekaligus. Berkas src/main.rs menjadi crate biner yang memuat fungsi main sebagai titik masuk program, sedangkan src/lib.rs menjadi crate pustaka yang berakar pada modul bernama sesuai nama paketnya. Kedua berkas ini menjadi akar modul masing-masing: deklarasi mod pada src/lib.rs mencari berkas di dalam src/. Biner mengakses pustaka melalui nama paket, bukan melalui jalur crate::.
Proyek contoh berikut bernama jadwal-piket dan terdiri atas empat berkas. Berkas Cargo.toml menyatakan identitas paket beserta edisi bahasanya, src/lib.rs mengekspos modul tugas dan fungsi sapa, dan src/tugas.rs memuat implementasi modul tersebut. Berkas src/main.rs menjadi program utama yang memakai modul tugas dan fungsi sapa.
[package]
name = "jadwal-piket"
version = "0.1.0"
edition = "2021"// tidak-dijalankan sendirian oleh rustc karena modul tugas berada di berkas lain.
//! Pustaka jadwal piket: penyusun daftar tugas harian anggota.
pub mod tugas;
/// Menyapa anggota yang mulai bertugas.
pub fn sapa(nama: &str) -> String {
format!("Selamat pagi, {nama}!")
}// tidak-dijalankan sendirian oleh rustc karena berkas ini bagian pustaka tanpa fungsi main.
//! Daftar tugas harian anggota piket.
/// Satu baris tugas beserta penanggung jawabnya.
pub struct Tugas {
pub nama: String,
pub penanggung: String,
}
/// Menyusun jadwal piket dengan tugas yang dibagi bergilir ke anggota.
pub fn susun(daftar: &[&str], anggota: &[&str]) -> Vec<Tugas> {
daftar
.iter()
.enumerate()
.map(|(i, nama)| Tugas {
nama: (*nama).to_string(),
penanggung: anggota[i % anggota.len()].to_string(),
})
.collect()
}// tidak-dijalankan sendirian oleh rustc karena memakai crate pustaka jadwal_piket dari berkas lain.
use jadwal_piket::tugas::susun;
fn main() {
println!("{}", jadwal_piket::sapa("piket pagi"));
let anggota = ["Ali", "Budi", "Citra"];
for tugas in susun(&["menyapu", "mengepel", "menyiram"], &anggota) {
println!("{} - {}", tugas.nama, tugas.penanggung);
}
}Baris komentar pada awal tiap berkas Rust menyatakan bahwa berkas tersebut tidak dapat dikompilasi sendirian oleh rustc, karena pada proyek multi-berkas compiler membutuhkan seluruh berkas yang saling merujuk. Pembangunan lengkap dilakukan Cargo, sehingga verifikasi eksekusi pada blok berikut memakai proyek di luar pohon konten situs. Perintah rm -rf menjaga blok tetap dapat dijalankan berulang, lalu seluruh berkas disalin dari bundle topik ke posisinya sebelum program dijalankan.
# siapkan proyek percobaan yang bersih di luar pohon konten
rm -rf /tmp/proyek-jadwal
cargo new /tmp/proyek-jadwal --bin -q --vcs none
# salin berkas proyek dari bundle ke posisinya masing-masing
cp Cargo.toml /tmp/proyek-jadwal/Cargo.toml
cp src/lib.rs /tmp/proyek-jadwal/src/lib.rs
cp src/tugas.rs /tmp/proyek-jadwal/src/tugas.rs
cp src/main.rs /tmp/proyek-jadwal/src/main.rs
# bangun kedua crate lalu jalankan program utamanya
cargo run --manifest-path /tmp/proyek-jadwal/Cargo.toml -qSelamat pagi, piket pagi!
menyapu - Ali
mengepel - Budi
menyiram - CitraCargo membangun crate pustaka terlebih dahulu karena biner bergantung padanya, kemudian menyusun main.rs yang mengakses pustaka melalui nama paket. Pemisahan seperti ini membuat logika penyusunan jadwal dapat diuji atau dipakai ulang tanpa menjalankan program utama, sementara main.rs cukup mengurus alur dan keluaran program. Satu paket dengan dua crate adalah bentuk yang umum pada proyek Rust: pustaka menyimpan fungsi inti, biner menjadi pembungkus tipis yang membaca masukan dan mencetak hasil.
Pencarian berkas modul#
Deklarasi mod tugas; tanpa isi kurung kurawal memerintahkan compiler mencari isi modul pada berkas terpisah. Aturannya mengikuti posisi deklarasi: modul yang dideklarasikan pada src/lib.rs dicari pada src/tugas.rs atau src/tugas/mod.rs, sedangkan modul yang dideklarasikan pada berkas non-akar seperti src/tugas.rs dicari pada subdirektori bernama tugas di sampingnya. Kedua gaya penamaan sama-sama sah, dan konvensi yang berlaku pada satu proyek sebaiknya dipakai konsisten.
| Deklarasi | Lokasi berkas yang dicari |
|---|---|
mod tugas; pada src/lib.rs | src/tugas.rs atau src/tugas/mod.rs |
mod anggota; pada src/tugas.rs | src/tugas/anggota.rs atau src/tugas/anggota/mod.rs |
mod laci; pada src/tugas/mod.rs | src/tugas/laci.rs atau src/tugas/laci/mod.rs |
Apabila berkasnya tidak ada, compiler menghentikan pembangunan dengan error E0583 yang menyebutkan modul yang dicari sekaligus kedua lokasi yang diperiksanya. Diagram berikut memetakan hubungan antara deklarasi pada src/lib.rs dan berkas yang diambilnya, termasuk arah pemakaian dari main.rs ke pustaka.
Gaya src/tugas.rs untuk modul satu berkas dan src/tugas/mod.rs untuk modul yang berkembang menjadi direktori adalah kebiasaan yang umum. Aturan visibilitasnya sama dengan modul inline: item tanpa pub hanya terlihat oleh sesama penghuni modul tersebut dan turunannya, sedangkan item pub dapat diimpor dari crate lain yang kelak memakai pustaka ini. Karena itu pemilihan lokasi berkas murni perkara penataan, bukan mekanisme penyembunyian.
Direktori khusus lain#
Cargo mengenali beberapa direktori dengan peran tetap di samping src/. Direktori tests/ memuat pengujian integrasi yang dikompilasi sebagai crate terpisah dan memakai pustaka sebagaimana pengguna luar, sedangkan direktori examples/ memuat program contoh kecil yang dibangun dengan cargo build --examples. Berkas-berkas Rust di dalam src/bin/ menjadi biner tambahan: setiap berkas menghasilkan program sendiri yang ikut dibangun bersama paket. Direktori benches/ menyimpan pengukur kinerja yang dijalankan secara terpisah.
Seluruh hasil pembangunan berakhir pada satu direktori target/ di akar proyek, dibedakan menurut profile debug dan release. Direktori ini boleh dihapus kapan saja karena isinya murni hasil kompilasi yang dapat dibangun ulang, dan umumnya dikecualikan dari pelacakan versi. Pengetahuan tentang direktori-direktori ini cukup untuk membaca hampir semua proyek Rust yang dijumpai, karena struktur baku ini dipakai secara luas oleh ekosistem.
Kesalahan umum#
Mendeklarasikan modul tanpa berkasnya. Deklarasi mod tugas; tanpa src/tugas.rs maupun src/tugas/mod.rs menghasilkan error E0583 yang menghentikan pembangunan. Buat berkas pada salah satu lokasi yang disebut pesan error, atau hapus deklarasi apabila modulnya memang belum ditulis.
Memakai jalur crate:: dari biner ke pustaka. Penulisan crate::tugas::susun pada main.rs merujuk crate biner, bukan crate pustaka, sehingga compiler melaporkan jalur tidak dikenal. Gunakan nama paket sebagai awalan jalur, misalnya jadwal_piket::tugas::susun, karena crate:: selalu berarti akar crate tempat kode ditulis.
Mengubah item pustaka tanpa menandai pub. Fungsi pada src/lib.rs yang tidak bertanda pub tidak terlihat dari main.rs walaupun berada dalam satu paket. Tambahkan pub pada item yang menjadi antarmuka pustaka, dan biarkan sisanya privat agar rincian implementasi tidak bocor ke program utama.
Ringkasan#
- Satu paket dapat memuat crate biner dari
src/main.rsdan crate pustaka darisrc/lib.rs, dan biner mengakses pustaka lewat nama paket. - Deklarasi
mod nama;mengambil isi modul darisrc/nama.rsatausrc/nama/mod.rsmengikuti posisi deklarasi. - Direktori
tests/,examples/, dansrc/bin/memiliki peran baku pada proyek Cargo. - Pemisahan berkas tidak mengubah aturan visibilitas: tanpa
pub, item tetap privat bagi luar modulnya.
Referensi#
- Separating Modules into Different Files — The Rust Book
— Aturan pencarian berkas untuk deklarasi
mod. - Package Layout — The Cargo Book — Struktur direktori baku proyek Cargo.
- Creating a New Package — The Cargo Book — Pembuatan proyek baru beserta perbedaan templat biner dan pustaka.