Compiler Rust menolak kode yang salah, tetapi diam pada kode yang benar namun ditulis dengan cara yang berbelit: pembanding panjang koleksi lewat len(), loop yang mengindeks manual, atau if yang mengembalikan nilai bool secara eksplisit. Kode semacam itu dikompilasi lancar dan berjalan benar — kecuali bahwa koleksi kosong terdeteksi lebih lambat, iterator lebih efisien terlewat, dan pembaca berikutnya menebak-nebak maksud penulisnya. Alat yang ditugaskan menangkap kelas masalah ini bernama clippy, linter resmi Rust yang berjalan lewat perintah cargo clippy. Berbeda dari compiler yang bicara hitam-putih salah atau benar, clippy berbicara tentang idiom: cara yang paling lumrah dan paling dihargai komunitas untuk menyatakan satu maksud. Chapter ini menjalankan clippy pada program yang penuh code smell, membaca saran perbaikannya satu per satu, lalu membuktikan versi yang sudah diperbaiki lolos tanpa warning.

Masalah: Kode yang Benar tetapi Tidak Idiomatik #

Perbedaan compiler dan linter terletak pada pertanyaan yang diajukan. Compiler bertanya “apakah program ini valid?” — pelanggaran apa pun berakhir dengan error dan program tidak jadi. clippy bertanya “apakah program ini ditulis dengan baik?” — pelanggaran dilaporkan sebagai warning berikut saran perbaikannya, dan keputusan akhir tetap di tangan kita. Karena itu clippy tidak menggantikan compiler, melainkan berlapis di atasnya: seluruh pemeriksaan compiler tetap berjalan, ditambah ratusan pemeriksaan khusus idiom yang disebut lint. Setiap lint punya nama, misalnya clippy::len_zero atau clippy::needless_range_loop, dan laporan clippy selalu menyebut nama tersebut agar bisa dicari dokumentasinya.

clippy terpasang sebagai komponen toolchain. Bila cargo clippy belum tersedia di mesin, pasang dengan rustup component add clippy; bila sudah terpasang, perintah itu hanya memastikan komponen tetap terbaru.

Mekanisme: Lint Pass Tambahan di Atas Compiler #

clippy dibangun di atas infrastruktur compiler yang sama: alat ini mengompilasi crate dengan driver tambahan yang menjalankan lint pass ekstra setelah pemeriksaan standar selesai. Setiap lint pass memindai pola tertentu pada AST dan hasil analisis — misalnya ekspresi if yang kedua cabangnya mengembalikan bool literal, atau pembanding len() terhadap nol pada tipe yang punya method is_empty(). Ketika pola ditemukan, lint mengeluarkan warning berikut span (lokasi persis di source) dan, bila tersedia, machine-applicable suggestion — teks pengganti yang bisa diterapkan otomatis oleh cargo clippy --fix. Semua laporan muncul pada tahap Checking yang sama dengan kompilasi biasa, sehingga clippy cepat karena menumpuk pekerjaan yang sudah dikerjakan compiler.

Contoh: Tiga Code Smell dalam Satu Program #

Buat proyek baru, lalu isi src/main.rs dengan program daftar menu yang sengaja ditulis tanpa idiom:

rust
fn main() {
    let menu = [String::from("kopi susu"), String::from("teh tarik"), String::from("roti bakar")];

    // apakah daftar menu masih kosong?
    let kosong = if menu.len() > 0 { false } else { true };

    if kosong {
        println!("Menu belum tersedia");
    } else {
        for i in 0..menu.len() {
            println!("Menu nomor {}: {}", i + 1, menu[i]);
        }
    }
}

Program ini dikompilasi tanpa keluhan dan berjalan benar. Jalankan clippy untuk melihat apa yang tersembunyi di balik kompilasi bersih:

bash
cargo clippy

Output yang Dihasilkan:

text
    Checking inventaris v0.1.0 (/tmp/w5b-clippy)
warning: this if-then-else expression returns a bool literal
 --> src/main.rs:5:18
   |
5 |     let kosong = if menu.len() > 0 { false } else { true };
   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `menu.len() <= 0`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.99.0/index.html#needless_bool
   = note: `#[warn(clippy::needless_bool)]` on by default

warning: length comparison to zero
 --> src/main.rs:5:21
   |
5 |     let kosong = if menu.len() > 0 { false } else { true };
   |                     ^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!menu.is_empty()`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.99.0/index.html#len_zero
   = note: `#[warn(clippy::len_zero)]` on by default

warning: the loop variable `i` is used to index `menu`
  --> src/main.rs:10:18
   |
10 |         for i in 0..menu.len() {
   |                  ^^^^^^^^^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.99.0/index.html#needless_range_loop
   = note: `#[warn(clippy::needless_range_loop)]` on by default
help: consider using an iterator and enumerate()
   |
10 -         for i in 0..menu.len() {
10 +         for (i, <item>) in menu.iter().enumerate() {
   |

warning: `inventaris` (bin "inventaris") generated 3 warnings (run `cargo clippy --fix --bin "inventaris" -p inventaris -- ` to apply 2 suggestions)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s

Tiga warning dengan tiga lint berbeda. Mari kita bedah satu per satu:

  1. Baris 5 (let kosong = if menu.len() > 0 { false } else { true };): lint clippy::needless_bool menangkap pola if yang kedua cabangnya mengembalikan bool literal — false dan true. Ekspresi itu setara dengan perbandingan tunggal, dan saran pertamanya langsung: ganti menjadi menu.len() <= 0.
  2. Baris 5, span kedua (menu.len() > 0): lint clippy::len_zero menangkap pembandingan panjang koleksi terhadap nol. Sarannya lebih idiomatik daripada saran pertama: !menu.is_empty() — method is_empty() ada untuk persis keperluan ini dan bekerja untuk semua koleksi standar.
  3. Baris 10 (for i in 0..menu.len() {): lint clippy::needless_range_loop menangkap loop yang membuat variabel indeks hanya untuk mengindeks koleksi yang sama. Sarannya menunjukkan bentuk pengganti: for (i, <item>) in menu.iter().enumerate(), dengan <item> sebagai tempat kita menamai elemen iterasi.
  4. Baris terakhir (generated 3 warnings (run cargo clippy --fix ...)): clippy menghitung total temuan dan mengingatkan bahwa dua dari tiga saran bisa diterapkan otomatis lewat cargo clippy --fix — saran yang bersifat machine-applicable.
  5. Baris Finished: proses clippy selesai pada profil dev, sama seperti kompilasi biasa — linter ini tidak menghasilkan binary, hanya laporan.

Memperbaiki Sesuai Saran #

Mari kita terapkan ketiga saran, memilih is_empty() di posisi yang paling jelas dan enumerate() untuk loop:

rust
fn main() {
    let menu = [String::from("kopi susu"), String::from("teh tarik"), String::from("roti bakar")];

    if menu.is_empty() {
        println!("Menu belum tersedia");
    } else {
        for (i, item) in menu.iter().enumerate() {
            println!("Menu nomor {}: {}", i + 1, item);
        }
    }
}

Jalankan ulang clippy, lalu programnya:

bash
cargo clippy && cargo run

Output yang Dihasilkan:

text
    Checking inventaris v0.1.0 (/tmp/w5b-clippy)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/inventaris`
Menu nomor 1: kopi susu
Menu nomor 2: teh tarik
Menu nomor 3: roti bakar

Clippy kini diam — tidak ada satu warning pun — dan program mencetak hasil yang sama seperti sebelumnya. Perhatikan bahwa variabel kosong hilang sama sekali: kondisi is_empty() langsung dipakai sebagai kondisi if, dan variabel sementara item menggantikan pengindeksan menu[i]. Kode yang dihasilkan lebih pendek, tanpa pengindeksan manual, dan menyatakan maksudnya lebih lugas.

Menjadikan Warning sebagai Error #

Warning mudah diabaikan — terutama bila jumlahnya menumpuk. Perintah berikut membalik posisi clippy menjadi penjaga gerbang: semua warning diperlakukan sebagai error sehingga kompilasi gagal bila satu pun muncul:

bash
cargo clippy -- -D warnings

Semua yang setelah -- diteruskan ke clippy; -D warnings menetapkan deny untuk lint warnings (kelompok seluruh warning). Program yang sudah kita perbaiki lolos dari perintah ini tanpa keluaran selain Checking dan Finished, sementara versi awal yang penuh smell akan gagal di baris warning pertama. Disiplin inilah yang akan kita kunci permanen pada Chapter Integrasi CI: perintah yang sama dijalankan otomatis di setiap pull request.

Ringkasan #

cargo clippy menambahkan ratusan lint pass di atas compiler: pola non-idiomatik seperti needless_bool, len_zero, dan needless_range_loop dilaporkan sebagai warning berikut span persis dan saran pengganti — sebagian bahkan bisa diterapkan otomatis dengan cargo clippy --fix. Versi program yang mengikuti saran (is_empty(), iter().enumerate()) berjalan identik dan lolos cargo clippy -- -D warnings yang menjadikan warning sebagai error. clippy baru saja menyapa kita dengan nama-nama lint; Chapter berikutnya memetakan ratusan lint tersebut ke dalam kategori agar bisa diatur per kelompok.