Pada titik ini kita sudah memegang seluruh peralatan dasar Rust: ownership mengatur kepemilikan data, trait menyusun perilaku lintas tipe, generic dan newtype memberi kita cara memperketat tipe, dan Result mengemas kegagalan dengan rapi. Peralatan tersebut menyelesaikan masalah di dalam kode kita sendiri. Section ini mengalihkan perhatian ke arah yang berbeda: bagaimana kode kita dipakai orang lain. Setiap tanda tangan publik yang kita terbitkan — fungsi, method, struct, tipe error — adalah API, dan kualitas API menentukan apakah pemanggil berhasil dalam hitungan menit atau tersesat berhari-hari. Standar yang kita pegang sepanjang section ini ringkasnya satu kalimat: API yang baik mudah dipakai dan sulit disalahgunakan. Prinsip tersebut bukan gagasan baru, tetapi Rust memberi kita alat yang tidak dimiliki banyak bahasa lain — compiler yang bersedia menjadi penjaga kontrak.

API sebagai Kontrak #

API adalah seluruh bagian kode kita yang terlihat dari luar: nama fungsi, urutan dan tipe parameter, tipe kembalian, beserta dokumentasinya. Pemanggil tidak membaca isi fungsi kita; pemanggil hanya membaca tanda tangan. Ketika tanda tangan berbunyi fn transfer(amount: u64, recipient: u64), seluruh kontrak yang tersedia bagi pemanggil hanyalah “kirim dua angka”. Aturan tambahan seperti “amount tidak boleh nol” atau “recipient harus sudah terdaftar” tidak terlihat pada tanda tangan tersebut, sehingga aturan tersebut hanya bisa dilanggar, bukan dicegah.

Kesalahan pemakaian yang berulang-ulang bukan sepenuhnya kesalahan pemanggil. Ketika sepuluh orang melakukan kesalahan yang sama pada library yang sama, pola pemakaian yang salah itu adalah celah yang dibiarkan terbuka oleh API-nya. Desain API yang baik mempersempit celah sebelum kode sempat berjalan, dan Rust menyediakan mekanisme untuk mempersempit celah pada waktu kompilasi — bukan pada saat insiden terjadi di produksi.

Prinsip Pertama: Mudah Dipakai #

Masalah paling klasik pada API yang tidak enak dipakai adalah parameter posisi yang tidak menjelaskan dirinya sendiri. Empat parameter dengan tipe &str, bool, bool, bool membuat pemanggil harus menghafal urutan dan makna setiap argumen. Kompilator hanya memeriksa tipe, bukan makna, sehingga dua bool yang tertukar tempat tetap lolos pemeriksaan tipe. Mari kita lihat dua versi API pembuatan akun dalam satu program:

rust
// API versi A: parameter bebas, urutan boolean rawan tertukar
fn create_account_v1(name: &str, admin: bool, active: bool, verified: bool) -> String {
    format!(
        "Akun {} (admin: {}, aktif: {}, terverifikasi: {})",
        name, admin, active, verified
    )
}

// Opsi bernama untuk API versi B
struct AccountOptions {
    admin: bool,
    active: bool,
    verified: bool,
}

// API versi B: pilihan pemanggil dibaca sebagai nama, bukan posisi
fn create_account_v2(name: &str, options: AccountOptions) -> String {
    format!(
        "Akun {} (admin: {}, aktif: {}, terverifikasi: {})",
        name, options.admin, options.active, options.verified
    )
}

fn main() {
    let akun_a = create_account_v1("Budi", true, false, true);
    println!("{}", akun_a);

    let akun_b = create_account_v2(
        "Siti",
        AccountOptions { admin: false, active: true, verified: true },
    );
    println!("{}", akun_b);
}

Output yang Dihasilkan:

text
Akun Budi (admin: true, aktif: false, terverifikasi: true)
Akun Siti (admin: false, aktif: true, terverifikasi: true)

Mari kita bedah kode di atas:

  1. Baris 2 (fn create_account_v1(name: &str, admin: bool, active: bool, verified: bool)): tanda tangan versi A menerima empat argumen posisi. Kompilator hanya memastikan argumen pertama bertipe &str dan tiga sisanya bertipe bool; kompilator tidak tahu argumen bool mana yang bermakna admin dan mana yang bermakna terverifikasi.
  2. Baris 25 (create_account_v1("Budi", true, false, true)): pemanggil mengirim tiga bool berurutan. Nilai false di tengah bisa berarti akun tidak aktif atau tidak terverifikasi — maknanya hanya diketahui pembuat API dan orang yang menghafal dokumentasinya.
  3. Baris 10-14 (struct AccountOptions { ... }): versi B mengangkat tiga pilihan tersebut menjadi field struct. Setiap field wajib dituliskan namanya saat konstruksi, jadi makna setiap nilai dibaca langsung dari kode, bukan dari ingatan.
  4. Baris 30 (AccountOptions { admin: false, active: true, verified: true }): pemanggil versi B tidak mungkin menukar dua nilai tanpa menukarkan namanya sekaligus. Bentuk pemanggilan seperti ini juga tahan terhadap penambahan field baru, karena field baru tidak menggeser posisi argumen lama.

Prinsip Kedua: Sulit Disalahgunakan #

Kemudahan dipakai hanya separuh prinsip. Separuh lainnya memastikan jalur pemakaian yang salah tertutup. Bentuk kebocoran yang sering muncul adalah stringly typed API: pilihan yang seharusnya terbatas direpresentasikan sebagai &str bebas. Salah ketik satu huruf tidak terdeteksi kompilator, dan program diam-diam jatuh ke cabang default. Berikut program yang membandingkan API string dengan API enum:

rust
// API bebas: string sebagai mode, salah ketik lolos dari kompilator
fn shutdown_v1(mode: &str) -> String {
    match mode {
        "force" => String::from("Mematikan paksa semua proses"),
        "graceful" => String::from("Menyelesaikan proses berjalan lalu mematikan"),
        _ => String::from("Mode tidak dikenal, memakai default: graceful"),
    }
}

// Pilihan mode diangkat menjadi tipe: salah ketik tidak bisa dikompilasi
enum ShutdownMode {
    Force,
    Graceful,
}

fn shutdown_v2(mode: ShutdownMode) -> String {
    match mode {
        ShutdownMode::Force => String::from("Mematikan paksa semua proses"),
        ShutdownMode::Graceful => String::from("Menyelesaikan proses berjalan lalu mematikan"),
    }
}

fn main() {
    // Salah ketik "gracefull" tetap berjalan, tapi diam-diam jatuh ke cabang default
    let hasil_a = shutdown_v1("gracefull");
    println!("Versi string: {}", hasil_a);

    let hasil_b = shutdown_v2(ShutdownMode::Graceful);
    println!("Versi enum: {}", hasil_b);

    let hasil_c = shutdown_v2(ShutdownMode::Force);
    println!("Versi enum: {}", hasil_c);
}

Output yang Dihasilkan:

text
Versi string: Mode tidak dikenal, memakai default: graceful
Versi enum: Menyelesaikan proses berjalan lalu mematikan
Versi enum: Mematikan paksa semua proses

Uraian komponen program di atas:

  1. Baris 2-8 (fn shutdown_v1(mode: &str) { ... }): versi string harus menangkap semua kemungkinan lewat cabang default _. Cabang default menjadi tempat bersembunyi bagi setiap salah ketik, termasuk "gracefull" yang dipanggil pada baris 25.
  2. Baris 11-14 (enum ShutdownMode { Force, Graceful }): versi enum membatasi nilai sah menjadi tepat dua varian. Nilai ketiga tidak bisa masuk tanpa melewati kompilator.
  3. Baris 16-21 (fn shutdown_v2(mode: ShutdownMode) { ... }): match pada enum tidak membutuhkan cabang default karena kompilator tahu seluruh varian sudah tertangani. Bila kelak kita menambah varian Scheduled, kompilator justru memaksa kita memperbarui match ini.
  4. Baris 28 dan 31 (shutdown_v2(ShutdownMode::Graceful) dan shutdown_v2(ShutdownMode::Force)): pemanggil mengetik nama varian secara penuh. Salah ketik pada nama varian gagal kompilasi, bukan diam-diam jatuh ke cabang default.

Kesalahan ketik pada varian enum memang tertangkap kompilator. Program berikut menuliskan Gracefull dengan dua huruf l, lalu dikompilasi dengan rustc --edition 2021 main.rs:

rust
enum ShutdownMode {
    Force,
    Graceful,
}

fn main() {
    let mode = ShutdownMode::Gracefull;
    let _ = mode;
}

Pesan error kompilator:

text
error[E0599]: no variant, associated function, or constant named `Gracefull` found for enum `ShutdownMode` in the current scope
 --> main.rs:7:30
  |
1 | enum ShutdownMode {
  | ----------------- variant, associated function, or constant named `Gracefull` not found for this enum
...
7 |     let mode = ShutdownMode::Gracefull;
  |                              ^^^^^^^^^ variant, associated function, or constant not found in `ShutdownMode`
  |
help: there is a variant with a similar name
  |
7 -     let mode = ShutdownMode::Gracefull;
7 +     let mode = ShutdownMode::Graceful;
  |

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0599`.

Error E0599 menegaskan bahwa nama Gracefull tidak ada pada enum ShutdownMode, lengkap dengan saran perbaikan menuju varian yang benar. Inilah bentuk nyata dari “sulit disalahgunakan”: kesalahan yang pada versi string hanya muncul sebagai perilaku aneh saat runtime, kini menjadi kegagalan kompilasi dengan petunjuk perbaikan.

Compiler sebagai Penjaga Kontrak #

Dua contoh di atas memperkenalkan pola pikir yang akan kita pakai di seluruh section ini: geser pemeriksaan dari runtime ke waktu kompilasi sedekat mungkin. Setiap chapter berikutnya menutup satu jenis celah dengan mekanisme Rust yang spesifik.

Celah pemakaian yang salahMekanisme Rust yang menutup celahChapter
Dua nilai sejenis tertukar posisiStruct dengan field bernamaPrinsip Desain API
Nilai tidak valid lolos ke konfigurasiValidasi terkumpul di build() yang mengembalikan ResultBuilder Pattern
Langkah dipanggil tidak berurutanTipe berbeda per state, method hanya tersedia pada state yang benarTypestate Pattern
Operasi sekali-jalan dipanggil ulangMethod mengonsumsi self, ownership move mematikan nilaiMengonsumsi self
Data yang melanggar aturan bisnis beredar bebasNewtype dengan konstruktor tervalidasiNewtype untuk Validasi Domain
Pesan error membocorkan detail internalError surface yang aman lewat DisplayDesain Error pada API
Penamaan membingungkan pemanggilKonvensi penamaan komunitasRust API Guidelines

Pola pada tabel tersebut selalu sama: temukan invariants yang selama ini hanya hidup di dokumentasi atau di pemeriksaan runtime, lalu angkat invariants tersebut menjadi bagian dari tipe. Setelah aturan menjadi tipe, kompilator yang menjaganya — setiap pemanggilan yang melanggar ditolak dengan pesan error, bukan dibiarkan lewat untuk menjadi bug di produksi.

Ringkasan #

Desain API di Rust berdiri di atas dua prinsip: mudah dipakai berarti tanda tangan menjelaskan dirinya sendiri melalui nama yang baik dan tipe yang tepat, dan sulit disalahgunakan berarti jalur pemakaian yang salah ditutup sebelum program berjalan. Parameter boolean berurutan kita ganti dengan struct bernama; pilihan string bebas kita ganti dengan enum, dan salah ketik varian ditolak kompilator dengan E0599 lengkap beserta saran perbaikan. Enam chapter berikutnya menerapkan pola pikir tersebut pada satu jenis celah demi satu jenis celah, dimulai dari konfigurasi bertahap yang tervalidasi penuh lewat Builder Pattern.