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:

PanduanAturan intiContoh
C-CASEFungsi, method, variabel memakai snake_case; tipe memakai PascalCase; konstanta memakai SCREAMING_SNAKE_CASEfn send_report, struct ServerConfig, const MAX_PORT
C-CRATE-NAMENama crate snake_case, pendek, tanpa campuran huruf besarcrate katalog, bukan Katalog atau desainApi
C-GETTERGetter diberi nama seperti field, tanpa awalan get_; awalan get_ khusus untuk operasi yang bisa gagal (Option/Result)book.pages(), map.get(key)
C-CONVERTas_ untuk konversi murah pinjam-ke-pinjam, to_ untuk konversi berbiaya pinjam-ke-memiliki, into_ untuk konsumsi selfstr::as_bytes, str::to_string, Option::into_inner
C-COMMON-TRAITSTipe publik mengimplementasikan Debug; tambahkan Default bila ada nilai wajar#[derive(Debug, Default)]
C-DOCSetiap item publik didokumentasikan; contoh dalam /// ikut menjadi testdoc 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:

rust
fn SendReport() -> u32 {
    21
}

fn main() {
    println!("{}", SendReport());
}

Pesan peringatan setelah perintah rustc --edition 2021 main.rs:

text
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 emitted

Peringatan 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:

rust
/// 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:

rust
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:

bash
cargo run --quiet

Output yang Dihasilkan:

text
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:

  1. Baris 1-10 pada lib.rs (doc comment struct): setiap item publik membawa dokumentasi /// — aturan C-DOC. Blok kode di dalamnya dimulai dengan use katalog::Book; karena doctest dikompilasi sebagai program terpisah yang hanya melihat API publik crate.
  2. Baris 11 (#[derive(Debug, Default)]): tipe publik mengimplementasikan Debug agar pemanggil bisa mencetak isinya saat debugging — C-COMMON-TRAITS. Default hadir karena Book punya nilai wajar: judul kosong dan nol halaman, terbukti pada output Book { title: "", pages: 0 }.
  3. Baris 33-43 (title dan pages): getter diberi nama persis seperti field, tanpa get_. Pemanggil yang mencari jumlah halaman cukup menebak pages() — dan tebakan tersebut benar.
  4. Baris 45-48 (as_title_bytes(&self) -> &[u8]): awalan as_ menjanjikan konversi murah: judul dipinjam sebagai potongan byte tanpa alokasi baru, dan nilai kembalian menempel pada masa hidup book.
  5. Baris 50-53 (to_uppercase_title(&self) -> String): awalan to_ menandakan konversi berbiaya — String baru dialokasikan. Pemanggil membaca biaya langsung dari nama method.
  6. Baris 55-58 (into_title(self) -> String): awalan into_ menyatakan konsumsi self — pola yang persis sama dengan consuming method pada chapter Mengonsumsi self. Setelah pemanggilan pada main.rs baris 11, variabel book tidak bisa dipakai lagi.
  7. Baris 60-67 (get_discount(&self, tier: u32) -> Option<u8>): awalan get_ justru dipakai di sini karena operasinya bisa gagal — tier yang tidak dikenal menghasilkan None. Inilah pengecualian yang Guidelines izinkan: get_ untuk pencarian yang mungkin tidak menemukan apa pun, seperti HashMap::get.
  8. 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 berkat Debug.

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:

bash
cargo test

Output yang Dihasilkan:

text
   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.07s

Bagian 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.