Rust projelerinde kod kalitesi için iki resmî araç kullanılır: cargo fmt kodu Rust Style Guide'daki varsayılan stile göre yeniden biçimlendirir, cargo clippy ise sık yapılan hataları ve daha iyi yazılabilecek kalıpları yakalayan ek lint'ler çalıştırır. CI tarafında tipik kullanım cargo fmt --check ve cargo clippy -- -D warnings komutlarıdır: ilki biçim farkı varsa, ikincisi herhangi bir uyarı varsa sıfır olmayan bir çıkış koduyla biter. Hangi lint'in ne kadar sert olacağını da #[allow], #[warn], #[deny], #[expect] öznitelikleri ya da Cargo.toml içindeki [lints] tablosuyla ayarlarsınız.

Bu yazıdaki tüm çıktılar Rust 1.99.1 ile gelen rustfmt 1.10.0 ve clippy 0.1.99 sürümleriyle, gerçek bir Cargo projesinde alındı. Cargo belgeleri her iki komutun da Cargo'ya gömülü olmadığını, araç zinciriyle birlikte dağıtılan isteğe bağlı bileşenler olduğunu belirtir; The Rust Book ise ikisinin de standart Rust kurulumuyla geldiğini söyler. Kurulum ayrıntıları için rustup ve Cargo kurulumu yazısına bakabilirsiniz.

cargo fmt: biçimlendirmeyi tartışma konusu olmaktan çıkarmak

Rust Style Guide biçimlendirme aracı kullanmanın gerekçesini açıkça yazar: biçimlendirme mekanik bir iştir, zaman ve dikkat ister; ortak bir stile uymak, kimin hangi kuralı seveceği tartışmasını ortadan kaldırır ve yeni bir projeyi okumayı kolaylaştırır. Kitap da birçok ortak projenin bu tartışmayı bitirmek için rustfmt kullandığını, aracın yalnızca kod stilini değiştirmesi, anlamını değiştirmemesi gerektiğini belirtir. rustfmt ile cargo fmt arasındaki ilişki rustc ile cargo arasındaki ilişkiye benzer: biri tek dosya üzerinde ince kontrol verir, diğeri Cargo projesinin tüm hedeflerini tanır.

Style Guide'ın varsayılan stilinden birkaç temel kural: girinti için sekme değil boşluk kullanılır, her girinti düzeyi dört boşluktur, en fazla satır genişliği 100 karakterdir, görsel hizalama yerine blok girinti tercih edilir ve satır sonunda biten virgüllü listelerde son elemandan sonra da virgül konur. Bu sonuncusu, kopyala yapıştırı ve küçük farklar (diff) üretmeyi kolaylaştırdığı için seçilmiştir.

Aşağıdaki dosyayı bilerek kötü biçimlendirdik ve birkaç tipik hata da ekledik. Clippy bölümünde aynı dosyayı kullanacağız:

rust
fn toplam(v: &Vec<i32>) -> i32 {
    let mut t = 0;
    for i in 0..v.len() { t = t + v[i]; }
    return t;
}

fn main() {
    let sayilar = vec![1,2,3];
    let bos = String::from("");
    if bos.len() == 0 { println!("boş"); }
    println!("{}", toplam(&sayilar));
}

cargo fmt --check dosyaları değiştirmez, yalnızca farkları gösterir ve fark varsa 1 koduyla çıkar. CI işlerinde biçim denetimi için kullanılan budur:

bash
$ cargo fmt --check
Diff in src/main.rs:1:
 fn toplam(v: &Vec<i32>) -> i32 {
     let mut t = 0;
-    for i in 0..v.len() { t = t + v[i]; }
+    for i in 0..v.len() {
+        t = t + v[i];
+    }
     return t;
 }

Diff in src/main.rs:7:
 fn main() {
-    let sayilar = vec![1,2,3];
+    let sayilar = vec![1, 2, 3];
     let bos = String::from("");
-    if bos.len() == 0 { println!("boş"); }
+    if bos.len() == 0 {
+        println!("boş");
+    }
     println!("{}", toplam(&sayilar));
 }

$ echo $?
1

Çıktıdaki dosya yolu, okunabilirlik için kısaltıldı; araç tam yolu yazar. Parametresiz cargo fmt aynı değişiklikleri dosyalara uygular, ardından cargo fmt --check 0 koduyla döner. Biçimlendirici yalnızca boşlukları ve satır kırılımlarını değiştirdi; return t; gibi gereksiz ama geçerli yapılara dokunmadı. Onlar Clippy'nin işi.

Style edition ve rustfmt.toml

Rust 2024 ile biçimlendirici tarafında da bir değişiklik geldi: style edition. Edition Guide'a göre rustfmt bir biçimlendirme kararlılığı garantisi verir; daha yeni bir sürüm, önceki sürümün başarıyla biçimlendirdiği kodu değiştirmemelidir. Bu garanti stilin gelişmesini de kilitlediği için Style Guide, edition modeline bağlandı. Cargo.toml içinde edition = "2024" olan bir projede cargo fmt çalıştırmak 2024 style edition'ı kullanır. İstenirse ayrı bir değer rustfmt.toml veya .rustfmt.toml dosyasında verilebilir:

toml
style_edition = "2024"

Belge bu dosyayı özellikle önerir. Gerekçesi, birçok editörün kaydetme anında rustfmt aracını doğrudan çalıştırması ve bu durumda varsayılan olarak 2015 edition'ı kullanmasıdır; cargo fmt ise Cargo.toml içindeki edition değerine bakar. Dosyada style edition yazılı olursa editör çıktısı ile CI çıktısı aynı olur. 2024 style edition ile gelen biçim farkları 2024 edition yazısında özetleniyor.

cargo clippy: derleyicinin ötesindeki lint'ler

cargo clippy: derleyicinin ötesindeki lint'ler: needless_return, ptr_arg, needless_range_loop, assign_op_pattern

Kitap Clippy'yi, kodunuzu analiz edip sık yapılan hataları yakalayan ve kodu iyileştiren bir lint koleksiyonu olarak tanımlar. Clippy kodu derler gibi denetler ama çalıştırmaz. Yukarıdaki biçimlendirilmiş dosyada çalıştırdığımızda beş uyarı aldık:

bash
$ cargo clippy
warning: unneeded `return` statement
warning: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
warning: the loop variable `i` is only used to index `v`
warning: manual implementation of an assign operation
warning: length comparison to zero
warning: `kalite` (bin "kalite") generated 5 warnings (run `cargo clippy --fix --bin "kalite" -p kalite -- ` to apply 3 suggestions)

Burada yalnızca başlık satırlarını gösterdik. Gerçek çıktıda her uyarının altında ilgili kod satırı, lint'in adı (örneğin clippy::needless_return), lint'in varsayılan olarak açık olduğu notu ve çoğu zaman önerilen düzeltme yer alır. Uyarıları tek tek okuyalım:

  • needless_return: fonksiyonun son ifadesi zaten dönüş değeridir, return t; yerine t yeterli. İfade ve deyim farkı için fonksiyonlar yazısına bakın.
  • ptr_arg: &Vec<i32> yerine &[i32] almak fonksiyonu dizilerle ve dilimlerle de kullanılabilir yapar. Gerekçesi slice yazısında anlatılıyor.
  • needless_range_loop: indeksle döngü kurmak yerine yineleyici kullanın, örneğin for x in v.
  • assign_op_pattern: t = t + v[i] yerine t += v[i].
  • len_zero: bos.len() == 0 yerine bos.is_empty(); niyet daha açık okunur.

Her lint'in açıklamasını terminalden okuyabilirsiniz. cargo clippy --explain len_zero komutu lint'in ne yaptığını, neden sorun sayıldığını ve önerilen yazımı gösterir. Bu çıktıya göre len_zero, bazı yapıların is_empty sorusunu uzunluğu hesaplamaktan çok daha hızlı yanıtlayabildiğini ve is_empty kullanmanın niyeti netleştirdiğini gerekçe olarak verir.

Clippy'nin bazı lint'leri varsayılan olarak hata düzeyindedir. Kitaptaki örneği 1.99.1 ile denedik: pi sayısının yaklaşık değerini elle yazmak derlemeyi durdurur.

rust
fn main() {
    let pi = 3.1415;
    let r = 2.0;
    println!("alan: {}", pi * r * r);
}
// cargo clippy çıktısı:
// error: approximate value of `f{32, 64}::consts::PI` found

Çözüm, standart kütüphanedeki daha hassas sabiti kullanmaktır: let pi = std::f64::consts::PI;.

cargo clippy --fix ve cargo fix

Clippy'nin yardım metnine göre --fix bayrağı lint önerilerini otomatik uygular ve --no-deps ile --all-targets davranışlarını içerir. Proje bir sürüm kontrol deposunda değilse Cargo, değişikliklerin geri alınamayabileceği gerekçesiyle durur ve --allow-no-vcs ister. Denememizde --fix beş uyarıdan üçünü düzeltti: return kaldırıldı, t += v[i] yazıldı ve is_empty() kullanıldı. ptr_arg ve needless_range_loop uyarıları kaldı, çünkü bunlar fonksiyon imzasını veya döngü yapısını değiştirmeyi gerektiriyor ve otomatik uygulanabilir bir öneri sunmuyor; bu kısmı elle yaparsınız.

Derleyicinin kendi uyarıları için de benzer bir araç vardır. Kitap cargo fix komutunu, düzeltmesi açık olan derleyici uyarılarını otomatik uygulayan rustfix aracı olarak tanıtır. Gereksiz bir let mut x = 42; satırında cargo build "variable does not need to be mutable" uyarısı verir, cargo fix ise mut kelimesini kaldırır. Aynı komut --edition ile edition geçişlerinde de kullanılır.

Lint düzeyleri: allow, expect, warn, force-warn, deny, forbid

Lint düzeyleri: allow, expect, warn, force-warn, deny, forbid: allow, expect, warn, force-warn

rustc: Lint Levels sayfası lint'leri altı düzeyde sınıflandırır. Her lint'in bir varsayılan düzeyi vardır ve siz bunu değiştirebilirsiniz:

DüzeyEtkisiNot
allowLint hiçbir şey bildirmezmissing_docs gibi bazı lint'ler varsayılan olarak burada
expectLint susturulur ama tetiklenmezse uyarı verilirYalnızca #[expect] özniteliğiyle
warnUyarı verir, derleme sürerunused_variables gibi
force-warnUyarı verir, daha sonra değiştirilemezYalnızca --force-warn bayrağıyla
denyHata verir, derleme dururAltta allow ile gevşetilebilir
forbidHata verir, daha alt düzeye çekilemez--cap-lints ile yine sınırlanabilir

Düzeyi üç yerden verebilirsiniz. Komut satırında rustc için -A, -W, --force-warn, -D ve -F bayrakları vardır; Clippy'de bunları iki tireden sonra yazarsınız: cargo clippy -- -D warnings. Belgeye göre bayrakların sırası önemlidir ve aynı lint için son verilen kazanır; böylece önce bir grubu açıp sonra içindeki tek bir lint'i kapatabilirsiniz. Kaynak kodda ise #![warn(missing_docs)] gibi crate genelinde ya da #[allow(...)] gibi tek bir öğe üzerinde öznitelik kullanırsınız. Tüm lint öznitelikleri, mesajda gösterilecek bir reason parametresi de alabilir.

Uyarıları CI'da hataya çevirmek için kullandığımız komutun sonucu şöyle:

bash
$ cargo clippy -- -D warnings
error: unneeded `return` statement
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
error: the loop variable `i` is only used to index `v`
error: manual implementation of an assign operation
error: length comparison to zero
error: could not compile `kalite` (bin "kalite") due to 5 previous errors
$ echo $?
101

#[expect] özellikle kullanışlıdır: bir lint'i bilerek susturursunuz, kod değişip lint artık tetiklenmediğinde derleyici bunu "this lint expectation is unfulfilled" uyarısıyla bildirir. Böylece unutulmuş allow öznitelikleri birikmez. Denememizde #[expect(clippy::len_zero)] ekli bir fonksiyonu is_empty() kullanacak şekilde düzelttiğimizde tam olarak bu uyarıyı aldık.

Lint grupları birden fazla lint'i tek adla yönetmenizi sağlar. rustc: Lint Groups sayfasında unused, nonstandard-style, future-incompatible ve rust-2024-compatibility gibi gruplar listelenir; warnings grubu da uyarı düzeyindeki tüm lint'leri kapsar. Kurulu derleyicinizdeki güncel listeyi rustc -W help ile görebilirsiniz. Clippy kendi lint'lerini de gruplar; cargo clippy -- -W help çıktısında clippy::all, clippy::style, clippy::complexity, clippy::perf, clippy::correctness, clippy::suspicious, clippy::pedantic, clippy::nursery, clippy::cargo ve clippy::restriction gruplarını gördük.

Cargo.toml içinde [lints] tablosu

Lint düzeylerini her dosyanın başına yazmak yerine paket genelinde Cargo.toml içinde tanımlayabilirsiniz. Cargo: The Manifest Format belgesine göre tablo adı, lint adındaki :: işaretinden önceki araç adıdır; işaret yoksa araç rust kabul edilir. Yani unsafe_code lint'i [lints.rust], clippy::ptr_arg ise [lints.clippy] altına yazılır. Cargo bu ayarları yalnızca mevcut pakete uygular, bağımlılıklara uygulamaz; belgeye göre bu tablo Cargo 1.74'ten beri desteklenir.

toml
[lints.rust]
unsafe_code = "forbid"

[lints.clippy]
ptr_arg = "allow"
needless_range_loop = "deny"
pedantic = { level = "warn", priority = -1 }

priority alanı, hangi lint veya grubun hangisini ezeceğini belirleyen işaretli bir tamsayıdır. Düşük, özellikle negatif sayılar daha düşük önceliklidir ve araçlara komut satırında önce verilir. Bu yüzden bir grubu priority = -1 ile açıp aynı tablodaki tek tek lint ayarlarının grubu ezmesini sağlamak yaygın bir kalıptır.

Bu ayarların etkisini denedik. Varsayılan ayarda hiçbir uyarı vermeyen x as u32 dönüşümü, pedantic grubunu açınca casting `u64` to `u32` may truncate the value uyarısı üretti ve not satırında uyarının clippy::pedantic grubundan geldiği yazıyordu. Dönüşüm kuralları için From, Into ve as yazısına bakabilirsiniz. unsafe_code = "forbid" ayarı varken bir fonksiyona #[allow(unsafe_code)] eklemek ise error[E0453]: allow(unsafe_code) incompatible with previous forbid hatasıyla reddedildi; forbid düzeyi tam olarak bunun için vardır.

Bağımlılıkların uyarıları ve --cap-lints

Belgeye göre rustc bir lint tavanı belirleyen --cap-lints bayrağını destekler. Cargo bu bayrağı bağımlılıkları derlerken --cap-lints allow olarak verir; böylece kullandığınız crate'lerdeki uyarılar sizin derleme çıktınızı kirletmez. Bu, -D warnings ile çalışan bir CI'ın bir bağımlılıktaki uyarı yüzünden kırılmamasını da sağlar. Tek istisna force-warn düzeyidir; tavan onu bastırmaz.

Önerilen iş akışı

Önerilen iş akışı: Editörünüzde kaydetme anında biçimlendirmeyi açın ve projeye rustfmt.toml ile..., Geliştirirken sık sık cargo clippy çalıştırın; uyarıları okuyun, gerekirse...
  • Editörünüzde kaydetme anında biçimlendirmeyi açın ve projeye rustfmt.toml ile style edition ekleyin.
  • Geliştirirken sık sık cargo clippy çalıştırın; uyarıları okuyun, gerekirse --explain ile gerekçesini öğrenin.
  • Kasıtlı istisnalar için allow yerine expect ve reason kullanın.
  • Paket genelindeki kuralları [lints] tablosuna yazın; böylece herkes aynı ayarla çalışır.
  • CI'da cargo fmt --check ve cargo clippy -- -D warnings çalıştırın; testler için cargo test yazısına bakın. GitHub üzerinde otomasyon için GitHub Actions rehberi yol gösterebilir.

Sık yapılan hatalar

  • cargo fmt ile editörün doğrudan çağırdığı rustfmt arasındaki edition farkını gözden kaçırmak.
  • cargo clippy -D warnings yazmak; lint bayrakları iki tireden sonra gelir: cargo clippy -- -D warnings.
  • Her uyarıyı #[allow] ile susturmak; çoğu Clippy önerisi kodu gerçekten sadeleştirir.
  • --fix çalıştırmadan önce değişiklikleri sürüm kontrolüne almamak.
  • Bir grubu ve içindeki tek lint'i aynı öncelikle yazıp hangisinin kazanacağını belirsiz bırakmak; gruba negatif priority verin.
  • Clippy lint adlarını [lints.rust] altına yazmak; doğru tablo [lints.clippy].

Sık Sorulan Sorular

cargo check ile cargo clippy arasındaki fark nedir?

İkisi de kodu çalıştırılabilir dosya üretmeden denetler. cargo clippy buna ek olarak Clippy lint'lerini çalıştırır; yardım metni tüm seçenekler için cargo check --help çıktısına yönlendirir.

Clippy önerilerinin hepsine uymak zorunda mıyım?

Hayır. Lint'ler öneridir ve düzeylerini projenize göre ayarlayabilirsiniz. Önemli olan, bir lint'i kapatıyorsanız bunu bilinçli yapmak ve reason ile gerekçesini yazmaktır.

rustfmt ayarlarını değiştirebilir miyim?

Evet, rustfmt.toml dosyasıyla. Ancak Style Guide, topluluk genelinde tutarlılığın getirdiği faydanın en çok aracın varsayılan ayarlarıyla elde edildiğini belirtir. Ayarları değiştirmeden önce bu kazancı tartın.

Kendi belgelendirme eksiklerimi lint ile yakalayabilir miyim?

Evet. missing_docs lint'i varsayılan olarak kapalıdır; #![warn(missing_docs)] ile açtığınızda belgesiz her herkese açık öğe için uyarı alırsınız. rustdoc'a özgü lint'ler rustdoc yazısında anlatılıyor.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Rust öğrenme rehberi · Rust nedir? · Cargo.toml ve bağımlılıklar · Test yazma ve cargo test · rustdoc ile belgelendirme · GitHub Actions CI/CD