Ha eddig csak a cargo build-re és a cargo test-re támaszkodtál, itt az ideje, hogy megismerkedj a Clippy-vel. A Rust fordító már önmagában is szigorú, de a Clippy egy szinttel feljebb viszi a dolgot: olyan mintázatokat is felismer, amik technikailag helyesek, de nem idiomatikusak, feleslegesen lassúak, vagy egyszerűen csak zavaróak egy másik fejlesztő számára.

Miért van szükséged a Clippyre?

A Rust compiler feladata, hogy a memóriabiztonságot és a típusbiztonságot garantálja. Ez viszont nem jelenti azt, hogy a kódod jó is. Gondolj bele: egy if x == true { ... } szintaktikailag tökéletes, de senki sem írna ilyet, ha tudná, hogy elég annyi, hogy if x { ... }. A Clippy pontosan ezeket a mintázatokat kapja el, mielőtt code review-ban valaki más venné észre.

Tipp

A Clippy nevét a paperclip-ből (gemkapocs) kapta, utalva a régi Microsoft Office segédre — csak itt a segéd tényleg hasznos tanácsokat ad.

Telepítés és első futtatás

Ha rustup-ot használsz (és valószínűleg igen), a Clippy telepítése egyetlen parancs:

rustup component add clippy

Ezután bármelyik projekted gyökerében lefuttathatod:

cargo clippy

Ez alapból minden targetet átvizsgál a jelenlegi feature-flag konfigurációval. Ha teljesebb lefedettséget szeretnél (tesztek, példák, benchmarkok is), érdemes ezt használni:

cargo clippy --all-targets --all-features

Nézzünk egy egyszerű példát, amit a Clippy azonnal kiszúr:

fn calculate_area(width: f64, height: f64) -> f64 {
    let result = width * height;
    return result;
}

fn main() {
    let area = calculate_area(3.0, 4.0);
    if area == 12.0 {
        println!("Pontosan 12!");
    }
}

Ennél a kódnál a Clippy két dolgot is jelezni fog: a felesleges return-t a függvény végén (clippy::needless_return), illetve azt, hogy lebegőpontos számokat egyenlőségre hasonlítasz, ami hibás eredményekhez vezethet (clippy::float_cmp). Ez utóbbi egy klasszikus kezdő hiba — a lebegőpontos aritmetika pontatlanságai miatt ritkán van értelme ==-t használni két f64 érték között.

Lint kategóriák: correctness, style, complexity, pedantic

A Clippy lintek nincsenek egy kalap alatt — csoportokba vannak rendezve, és ez nagyon fontos, mert eltérő a szigorúságuk és a hamis pozitív arányuk:

  • correctness: ezek szinte biztosan hibák. A cargo clippy alapból deny szinten futtatja őket, tehát build hibát is okozhatnak.
  • suspicious: gyanús minták, amik gyakran hibára utalnak, de nem mindig.
  • style: idiomatikus Rust írásmódra vonatkozó javaslatok — pl. a fenti needless_return.
  • complexity: feleslegesen bonyolult kódra figyelmeztet, amit egyszerűbben is meg lehetne írni.
  • perf: teljesítmény szempontból szuboptimális mintázatok, pl. felesleges klónozás.
  • pedantic: extra szigorú, stílusfüggő szabályok, amiket alapból NEM futtat a Clippy, mert sok projektben túl zajosak lennének.
  • nursery: kísérleti lintek, amik még fejlesztés alatt állnak, instabilak lehetnek.
  • restriction: erősen szubjektív, projekt-specifikus tiltások (pl. unwrap tiltása), ezeket sosem kapcsolod be csoportosan.

Alapból a cargo clippy a correctness, suspicious, style, complexity és perf csoportokat futtatja (ez a clippy::all csoport). Ha szeretnéd bekapcsolni a pedantic szabályokat is, ezt explicit meg kell tenned:

cargo clippy -- -W clippy::pedantic
Figyelem

A pedantic csoport tényleg pedáns. Ha egy nagyobb, régebbi kódbázison kapcsolod be először, könnyen kaphatsz több száz figyelmeztetést. Ne ess pánikba — fokozatosan vezesd be, modulonként.

Finomhangolás projekt szinten: clippy.toml és lint attribútumok

A legtöbb csapatnál a lint szabályokat nem a parancssorban akarod tartani, hanem a kódban, verziókövetve. Erre két eszközöd van: a lint attribútumok a kódban, és a clippy.toml konfigurációs fájl a projekt gyökerében.

A src/lib.rs vagy src/main.rs tetején beállíthatod, mely csoportokat szeretnéd figyelmeztetésként vagy hibaként kezelni:

#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
#![deny(clippy::correctness)]

fn divide(a: i32, b: i32) -> i32 {
    a / b
}

Ez a beállítás azt jelenti, hogy a correctness kategóriájú lintek hibát dobnak (a build megbukik), míg a pedantic és nursery csak figyelmeztetést ad. Ez egy jó kompromisszum: a valódi hibákat kizárod a build-ből, de a stilisztikai javaslatokat nem kényszeríted ki azonnal.

A clippy.toml fájllal pedig egyes lintek viselkedését testre szabhatod. Például, ha nem szeretnéd, hogy a too_many_arguments lint már 5 paraméternél panaszkodjon:

# clippy.toml
too-many-arguments-threshold = 8
cognitive-complexity-threshold = 25

Ez különösen hasznos, ha a csapatod más konvenciókat követ, mint a Clippy alapértelmezett feltételezései.

Megjegyzés

A clippy.toml csak bizonyos lintek paramétereit tudja hangolni, nem kapcsol be vagy ki l797kombinációkat — arra a #![warn(...)] / #![allow(...)] attribútumok valók.

Clippy a CI pipeline-ban

Ha a Clippy futtatása csak a fejlesztők jóindulatán múlik, előbb-utóbb valaki simán elfelejti lefuttatni. Éppen ezért érdemes a CI pipeline-ba beépíteni, hogy a pull request-ek automatikusan ellenőrizve legyenek.

GitHub Actions esetén egy egyszerű job így nézhet ki:

name: CI

on: [push, pull_request]

jobs:
  clippy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          components: clippy
          override: true
      - name: Run Clippy
        run: cargo clippy --all-targets --all-features -- -D warnings

A -D warnings kapcsoló kulcsfontosságú: ez minden figyelmeztetést hibává alakít, így a build megbukik, ha bármilyen lint triggerelődik. Ez elsőre ijesztőnek tűnhet, de hosszú távon garantálja, hogy a kódbázisod tiszta marad.

Jó tudni

Ha csapatban dolgozol, célszerű a -D warnings-t csak a clippy::all csoportra alkalmazni, és a pedantic/nursery figyelmeztetéseket külön, nem blokkoló lépésként futtatni. Így nem állítod meg a fejlesztést minden apró stilisztikai észrevétel miatt.

Hamis pozitívok kezelése

Előbb-utóbb találkozol olyan esettel, amikor a Clippy hibásan jelez valamit — vagy technikailag igaza van, de a te konkrét helyzetedben a javaslata nem alkalmazható. Ilyenkor az #[allow(...)] attribútum a barátod, de érdemes minél szűkebb hatókörben használni.

#[allow(clippy::too_many_arguments)]
fn create_report(
    title: &str,
    author: &str,
    date: &str,
    department: &str,
    priority: u8,
    tags: &[&str],
    body: &str,
    footer: &str,
) -> String {
    format!("{title} - {author} ({date})")
}

Itt tudatosan elfogadjuk, hogy a függvénynek sok paramétere van (esetleg egy builder pattern-re való átállás később megéri, de most nem). A kulcs, hogy ne a fájl tetején, hanem közvetlenül a releváns kód fölött helyezd el az allow-t — így bárki, aki később olvassa a kódot, pontosan látja, mi és miért van kikapcsolva.

Figyelem

Kerüld a #![allow(clippy::all)] használatát a fájl vagy a crate tetején! Ez gyakorlatilag kikapcsolja a teljes Clippy-t az adott hatókörben, és elveszíted az összes hasznos figyelmeztetést is, nem csak azt az egyet, ami zavart.

Ha egy adott lintet a teljes projektben szeretnél letiltani (mert pl. nem illik a csapat konvenciójához), azt is megteheted a lib.rs-ben:

#![allow(clippy::module_name_repetitions)]

Ez a lint pl. azt jelzi, ha egy modulban lévő típus neve megismétli a modul nevét (pl. report::Report), ami sok esetben teljesen ésszerű elnevezési konvenció, csak a Clippy pedantic csoportja szigorúan veszi.

Összefoglalás

A Clippy nem egy plusz teher, hanem egy ingyenes senior code reviewer, aki sosem fárad el, és mindig ugyanazokkal a szemüvegekkel néz a kódra. Kezdd az alapokkal (cargo clippy), ismerkedj meg a lint kategóriákkal, majd fokozatosan vezesd be a pedantic szabályokat is, ahol értelmes. Építsd be a CI-ba, hogy senki ne tudja megkerülni, és tanuld meg tudatosan, célzottan használni az #[allow(...)] attribútumot azokra az esetekre, ahol a Clippy javaslata nem illik a helyzetedhez. Ezekkel a lépésekkel a kódbázisod nemcsak fordítani fog, hanem valódi, idiomatikus Rust kód lesz — amit bárki könnyen olvashat és karbantarthat utánad.