Enam chapter sebelumnya memberi kita pola struktural untuk menutup celah pemakaian: builder, typestate, consuming method, newtype, dan error surface. Satu hal terakhir yang membuat API terasa “Rust asli” bukan pola besar, melainkan kepatuhan pada konvensi kecil: pemanggil harus bisa menebak nama method kita tanpa membuka dokumentasi. Komunitas Rust merumuskan kebiasaan-kebiasaan tersebut dalam dokumen Rust API Guidelines — kumpulan rekomendasi berlabel C-XXX yang menjadi rujukan bagi penulis library, termasuk penulis pustaka standar. Pada chapter penutup section ini kita rangkum bagian guidelines yang paling sering bersentuhan dengan kode sehari-hari: penamaan, getter, awalan konversi as_/to_/into_, trait umum, dan dokumentasi — lalu mempraktikkan semuanya dalam satu crate kecil bernama katalog yang dokumentasinya ikut teruji.
Peta Singkat Rust API Guidelines #
Dokumen lengkapnya panjang, tetapi inti yang paling sering dipakai bisa diringkas dalam tabel berikut:
| Panduan | Aturan inti | Contoh |
|---|---|---|
| C-CASE | Fungsi, method, variabel memakai snake_case; tipe memakai PascalCase; konstanta memakai SCREAMING_SNAKE_CASE | fn send_report, struct ServerConfig, const MAX_PORT |
| C-CRATE-NAME | Nama crate snake_case, pendek, tanpa campuran huruf besar | crate katalog, bukan Katalog atau desainApi |
| C-GETTER | Getter diberi nama seperti field, tanpa awalan get_; awalan get_ khusus untuk operasi yang bisa gagal (Option/Result) | book.pages(), map.get(key) |
| C-CONVERT | as_ untuk konversi murah pinjam-ke-pinjam, to_ untuk konversi berbiaya pinjam-ke-memiliki, into_ untuk konsumsi self | str::as_bytes, str::to_string, Option::into_inner |
| C-COMMON-TRAITS | Tipe publik mengimplementasikan Debug; tambahkan Default bila ada nilai wajar | #[derive(Debug, Default)] |
| C-DOC | Setiap item publik didokumentasikan; contoh dalam /// ikut menjadi test | doc comment dengan contoh assert_eq! |
Kolom contoh sengaja memakai nama dari pustaka standar dan dari crate yang akan kita bangun sebentar lagi, agar aturan dan praktik terlihat utuh dalam satu tarikan napas. Label C- pada setiap entri berasal dari dokumen aslinya — C-CASE, C-GETTER, dan seterusnya — sehingga saat kita membaca kode library populer atau membuka Guidelines yang lengkap, kita bisa langsung memetakan setiap kebiasaan ke label panduannya.
Mengapa konvensi sekecil ini penting? Karena API publik adalah bahasa yang kita ajarkan kepada pemanggil. Ketika seluruh ekosistem menamai getter tanpa get_ dan konversi dengan as_/to_/into_, pemanggil yang sudah terbiasa dengan Vec dan String bisa memakai API kita tanpa membaca satu baris dokumentasi pun — cukup dengan menebak sesuai kebiasaan yang berlaku. Konvensi mempersingkat jarak dari “ingin melakukan sesuatu” ke “menemukan method yang tepat”, dan jarak tersebut adalah pengalaman pemakaian yang sesungguhnya.
Penamaan yang Ditegakkan Compiler #
Menariknya, sebagian aturan penamaan bukan sekadar kesepakatan sosial — compiler ikut menjaganya lewat lint bawaan. Program berikut memakai nama fungsi PascalCase:
fn SendReport() -> u32 {
21
}
fn main() {
println!("{}", SendReport());
}Pesan peringatan setelah perintah rustc --edition 2021 main.rs:
warning: function `SendReport` should have a snake case name
--> main.rs:1:4
|
1 | fn SendReport() -> u32 {
| ^^^^^^^^^^ help: convert the identifier to snake case: `send_report`
|
= note: `#[warn(non_snake_case)]` (part of `#[warn(nonstandard_style)]`) on by default
warning: 1 warning emittedPeringatan non_snake_case aktif secara bawaan, lengkap dengan saran perbaikan send_report. Lint kembar seperti non_camel_case_types menjaga penamaan struct dan enum dari sisi sebaliknya. Sisa panduan — getter, awalan konversi, trait umum, dokumentasi — tidak dijaga lint bawaan, sehingga kepatuhannya menjadi tanggung jawab kita; alat bantu seperti clippy membantu menangkap sebagian pelanggaran, sesuai materi section Kualitas Kode.
Crate Kecil yang Mengikuti Panduan #
Kita praktikkan seluruh tabel di atas dalam crate katalog dengan dua file: src/lib.rs menjadi target library yang menampung tipe publik, dan src/main.rs menjadi binary pemanggil. Pemisahan ini penting karena doctest hanya hidup pada target library, sesuai pembahasan struktur proyek pada section Modul dan Visibilitas. Berikut isi src/lib.rs:
/// Merepresentasikan satu buku pada katalog toko.
///
/// # Contoh
///
/// ```
/// use katalog::Book;
///
/// let book = Book::new("Belajar Rust", 210);
/// assert_eq!(book.pages(), 210);
/// ```
#[derive(Debug, Default)]
pub struct Book {
title: String,
pages: u32,
}
impl Book {
/// Membuat buku baru dengan judul dan jumlah halaman.
///
/// ```
/// use katalog::Book;
///
/// let book = Book::new("Belajar Rust", 210);
/// assert_eq!(book.pages(), 210);
/// ```
pub fn new(title: &str, pages: u32) -> Self {
Self {
title: title.to_string(),
pages,
}
}
/// Mengembalikan judul buku sebagai referensi.
///
/// Nama method mengikuti C-GETTER: nama field tanpa awalan `get_`.
pub fn title(&self) -> &str {
&self.title
}
/// Mengembalikan jumlah halaman buku.
pub fn pages(&self) -> u32 {
self.pages
}
/// Pinjam judul sebagai byte tanpa alokasi baru (C-CONVERT: `as_`).
pub fn as_title_bytes(&self) -> &[u8] {
self.title.as_bytes()
}
/// Salin judul dalam huruf kapital, ada alokasi baru (C-CONVERT: `to_`).
pub fn to_uppercase_title(&self) -> String {
self.title.to_uppercase()
}
/// Pindahkan kepemilikan judul keluar dari buku (C-CONVERT: `into_`).
pub fn into_title(self) -> String {
self.title
}
/// Diskon untuk tier tertentu; bisa gagal, karenanya awalan `get_`.
pub fn get_discount(&self, tier: u32) -> Option<u8> {
match tier {
1 => Some(10),
2 => Some(20),
_ => None,
}
}
}Dan berikut pemanggilnya di src/main.rs:
use katalog::Book;
fn main() {
let book = Book::new("Belajar Rust untuk Pemula", 210);
println!("Judul: {}", book.title());
println!("Halaman: {}", book.pages());
println!("Judul dalam byte: {}", book.as_title_bytes().len());
println!("Judul teriak: {}", book.to_uppercase_title());
let judul = book.into_title();
println!("Judul yang dipindah: {}", judul);
let buku_lain = Book::default();
println!("Buku default: {:?}", buku_lain);
match Book::new("Rahasia API", 80).get_discount(2) {
Some(diskon) => println!("Diskon tier 2: {}%", diskon),
None => println!("Tidak ada diskon untuk tier ini"),
}
}Jalankan dengan perintah berikut dari direktori crate:
cargo run --quietOutput yang Dihasilkan:
Judul: Belajar Rust untuk Pemula
Halaman: 210
Judul dalam byte: 25
Judul teriak: BELAJAR RUST UNTUK PEMULA
Judul yang dipindah: Belajar Rust untuk Pemula
Buku default: Book { title: "", pages: 0 }
Diskon tier 2: 20%Mari kita bedah kode di atas:
- Baris 1-10 pada
lib.rs(doc comment struct): setiap item publik membawa dokumentasi///— aturan C-DOC. Blok kode di dalamnya dimulai denganuse katalog::Book;karena doctest dikompilasi sebagai program terpisah yang hanya melihat API publik crate. - Baris 11 (
#[derive(Debug, Default)]): tipe publik mengimplementasikanDebugagar pemanggil bisa mencetak isinya saat debugging — C-COMMON-TRAITS.Defaulthadir karenaBookpunya nilai wajar: judul kosong dan nol halaman, terbukti pada outputBook { title: "", pages: 0 }. - Baris 33-43 (
titledanpages): getter diberi nama persis seperti field, tanpaget_. Pemanggil yang mencari jumlah halaman cukup menebakpages()— dan tebakan tersebut benar. - Baris 45-48 (
as_title_bytes(&self) -> &[u8]): awalanas_menjanjikan konversi murah: judul dipinjam sebagai potongan byte tanpa alokasi baru, dan nilai kembalian menempel pada masa hidupbook. - Baris 50-53 (
to_uppercase_title(&self) -> String): awalanto_menandakan konversi berbiaya —Stringbaru dialokasikan. Pemanggil membaca biaya langsung dari nama method. - Baris 55-58 (
into_title(self) -> String): awalaninto_menyatakan konsumsiself— pola yang persis sama dengan consuming method pada chapter Mengonsumsi self. Setelah pemanggilan padamain.rsbaris 11, variabelbooktidak bisa dipakai lagi. - Baris 60-67 (
get_discount(&self, tier: u32) -> Option<u8>): awalanget_justru dipakai di sini karena operasinya bisa gagal — tier yang tidak dikenal menghasilkanNone. Inilah pengecualian yang Guidelines izinkan:get_untuk pencarian yang mungkin tidak menemukan apa pun, sepertiHashMap::get. - Baris 1-21 pada
main.rs: pemanggil memakai seluruh API tanpa membuka dokumentasi — nama-nama method mengikuti pola pustaka standar yang sudah dikenalnya, dan{:?}bekerja berkatDebug.
Dokumentasi idiomatik juga punya struktur yang dikenal rustdoc. Judul bagian seperti # Contoh, # Panics, dan # Errors ditulis dengan penanda Markdown biasa di dalam doc comment: bagian # Panics menyatakan kondisi yang bisa memicu panic, dan bagian # Errors menjelaskan varian error yang bisa muncul dari Result — kebiasaan yang melengkapi error surface dari chapter Desain Error pada API. Doc comment juga bisa memuat tautan ke item lain di crate yang sama, misalnya menuju Book::new, sehingga halaman dokumentasi yang dihasilkan saling terhubung seperti satu buku panduan utuh.
Doctest: Dokumentasi yang Ikut Dites #
Blok kode dalam doc comment bukan hiasan. Perintah cargo test mengkompilasi setiap blok tersebut sebagai program tersendiri, menjalankannya, dan menggagalkan test bila hasilnya tidak sesuai:
cargo testOutput yang Dihasilkan:
Compiling katalog v0.1.0 (/tmp/w6d-katalog)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.17s
Running unittests src/lib.rs (target/debug/deps/katalog-626f7e96cada9ab8)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/debug/deps/katalog-f1ad6cfa1ae07318)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests katalog
running 2 tests
test src/lib.rs - Book::new (line 20) ... ok
test src/lib.rs - Book (line 5) ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07sBagian akhir output menunjukkan kedua doctest lolos: contoh pada doc comment Book (baris 5 pada src/lib.rs) dan contoh pada Book::new (baris 20). Baris Compiling pada awal output menampilkan lokasi proyek sesuai mesin tempat kompilasi berjalan, jadi path-nya wajar berbeda di komputer kita masing-masing. Dengan mekanisme ini, dokumentasi tidak bisa berbohong: begitu pages() berubah perilaku, contoh assert_eq!(book.pages(), 210) gagal dan cargo test menolak hijau.
Ringkasan #
Rust API Guidelines mengikat seluruh pola section ini menjadi konvensi yang bisa ditebak pemanggil: penamaan snake_case dan PascalCase dijaga lint bawaan, getter tanpa get_ kecuali operasinya bisa gagal, awalan as_/to_/into_ mengomunikasikan biaya dan kepemilikan konversi, tipe publik membawa Debug dan bila layak Default, serta dokumentasi setiap item publik lengkap dengan contoh yang ikut teruji lewat doctest. Crate katalog membuktikan bahwa seluruh aturan tersebut bisa dipenuhi dalam satu file kecil tanpa biaya tambahan. Dengan prinsip, pola, dan konvensi yang kini lengkap di tangan kita, API yang kita terbitkan tidak hanya benar secara compiler — tetapi juga terasa seperti karya penulis Rust sungguhan.