- Rust'ın tip sistemini ve derleyicisini etkin biçimde kullanarak hataları önceden engelleyen kodlama alışkanlıkları tanıtılıyor
- Vektör indeksleme,
Default'un aşırı kullanımı, eksik match, gereksiz boolean parametreler gibi kırılgan kod kokusu (Code Smell) örnekleri sunuluyor ve alternatifleri açıklanıyor
- Temel ilke, yapıyı derleyicinin invariant'ları zorunlu kılacağı şekilde tasarlamak; bunun için pattern matching, özel alanlar ve
#[must_use] niteliği gibi araçlar kullanılıyor
TryFrom kullanımı, struct'ı tamamen parçalara ayırma, geçici değiştirilebilirlik, constructor doğrulaması gibi gerçek kod düzeyinde savunma teknikleri somut biçimde gösteriliyor
- Bu kalıplar, refactor sırasında kararlılığı korumak ve uzun vadeli bakım yapılabilirliği artırmak için kritik önem taşıyor
Savunmacı programlamaya genel bakış
// this should never happen yorumunun bulunduğu yerler, örtük invariant'ların bozulduğu noktaları gösterir
- Çoğu durumda geliştirici tüm sınır durumlarını ya da gelecekteki kod değişikliklerini hesaba katmaz
- Rust derleyicisi bellek güvenliğini garanti eder, ancak iş mantığı hataları yine de ortaya çıkabilir
- Yıllara yayılan pratik deneyimle edinilen küçük alışkanlık kalıpları (idiom) kod kalitesini büyük ölçüde artırır
Code Smell: vektör indeksleme
if !vec.is_empty() { let x = &vec[0]; } biçimi, uzunluk kontrolü ile indekslemeyi ayırdığı için çalışma zamanında panic riski taşır
- Slice pattern matching (
match vec.as_slice()) kullanıldığında derleyici tüm durumların kontrol edilmesini zorunlu kılar
- Boş vektör, tek öğe, yinelenen öğeler gibi tüm durumlar açıkça ele alınabilir
- Bu, derleyicinin invariant'ları garanti etmesini sağlayacak şekilde tasarlamanın tipik bir örneğidir
Code Smell: Default'un ölçüsüz kullanımı
..Default::default(), yeni alan eklendiğinde gözden kaçırma riski ve örtük değer atama sorunları doğurur
- Tüm alanları açıkça ilklendirmek, derleyicinin yeni alanların ayarlanmasını zorunlu kılmasını sağlar
let Foo { field1, field2, .. } = Foo::default(); biçimiyle varsayılan struct'ı parçalayıp seçici override yapmak mümkündür
- Varsayılanı koruma ile açık override arasında denge kurulur
Code Smell: kırılgan trait implementasyonu
- Struct alanlarını tamamen parçalayıp karşılaştırmak, yeni alan eklendiğinde derleme hatasıyla uyarı verilmesini sağlar
- Örneğin
PartialEq implementasyonunda let Self { size, toppings, .. } = self;
extra_cheese gibi yeni bir alan eklendiğinde karşılaştırma mantığının yeniden gözden geçirilmesi zorunlu hale gelir
- Aynı ilke
Hash, Debug, Clone gibi diğer trait'lere de uygulanabilir
Code Smell: From yerine TryFrom gerektiğinde
- Dönüşüm her zaman başarılı olmuyorsa
From yerine başarısızlık ihtimalini açık eden TryFrom kullanılmalıdır
unwrap_or_else kullanımı, potansiyel başarısızlığı gizleyen bir işaret olduğundan erken başarısızlık (fail fast) yaklaşımı daha güvenlidir
Code Smell: eksik match
_ => {} gibi catch-all pattern'ler, yeni variant eklendiğinde bir durumun atlanması riskini taşır
- Tüm variant'ları açıkça sıralamak, derleyicinin yeni case'lerin ele alınmadığını bildirmesini sağlar
- Aynı mantık
Variant3 | Variant4 biçiminde gruplanarak da kullanılabilir
Code Smell: _ placeholder'ının aşırı kullanımı
- Yalnızca
_ kullanmak, hangi değişkenin atlandığını belirsiz bırakır
has_fuel: _, has_crew: _ gibi açık adlarla okunabilirlik artırılabilir
Pattern: geçici değiştirilebilirlik (Temporary Mutability)
- Veri yalnızca ilklendirme sırasında değiştirilebilir olmalıysa
let mut data = ...; data.sort(); let data = data; biçimi kullanılabilir
- Blok scope kullanmak, geçici değişkenin dışarı sızmasını engeller
- Örneğin
let data = { let mut d = get_vec(); d.sort(); d };
- Birden fazla geçici değişken kullanılan ilklendirme sürecinde kapsamları net biçimde ayırmak mümkün olur
Pattern: constructor doğrulamasını zorunlu kılma
- Struct oluşturulurken doğrulama mantığından mutlaka geçilmesi sağlanır
_private: () alanı eklenirse dışarıdan doğrudan oluşturma mümkün olmaz
#[non_exhaustive] niteliği, crate dışından oluşturmayı engeller ve gelecekte genişleyebileceğine işaret eder
- İç modüllerde de bunu zorlamak için özel tip (Seal) içeren iç içe modül yapısı kullanılabilir
Seal yalnızca içeride var olduğundan new() dışında doğrudan oluşturma mümkün olmaz
- Alanları özel tutup getter sağlamak, değişmez durumun korunmasına yardımcı olur
- Uygulama ölçütleri
- Dış kodu engelleme:
_private ya da #[non_exhaustive]
- İç kodu engelleme: özel modül +
Seal
- Doğrulama mantığını derleyici düzeyinde bir güvenceye dönüştürme
Pattern: #[must_use] niteliğinden yararlanma
#[must_use], önemli dönüş değerlerinin göz ardı edilmesini önler
- Örneğin
#[must_use = "Configuration must be applied to take effect"]
- Kullanıcı dönüş değerini yok sayarsa derleyici uyarısı oluşur
Result gibi standart kütüphane türlerinde de yaygın kullanılan, basit ama güçlü bir savunma aracıdır
Code Smell: boolean parametreler
fn process_data(..., compress: bool, encrypt: bool, validate: bool) biçimi anlam belirsizliği ve sıra hatası riski taşır
enum Compression, enum Encryption gibi yapılarla niyet açık biçimde ifade edilir
- Birden çok seçenek varsa parametre struct'ı (Params struct) kullanılabilir
ProcessDataParams::production() gibi ön ayar metotlarıyla yeniden kullanılabilirlik artar
- Yeni seçenek eklendiğinde mevcut çağrı noktaları en az düzeyde etkilenir
Clippy lint'leriyle otomasyon
- Temel savunmacı kalıplar Clippy lint'leriyle otomatik olarak denetlenebilir
indexing_slicing: doğrudan indekslemeyi yasaklar
fallible_impl_from: From yerine TryFrom önerir
wildcard_enum_match_arm: _ pattern'ini yasaklar
fn_params_excessive_bools: aşırı boolean parametre kullanımına uyarı verir
must_use_candidate: #[must_use] adayı önerir
#![deny(clippy::...)] ya da Cargo.toml ayarlarıyla proje genelinde uygulanabilir
Sonuç
- Rust'ın tip sistemini ve derleyicisini etkin biçimde kullanarak invariant'ları açık, doğrulanabilir hale getirmek savunmacı programlamanın özüdür
- Bu kalıplar, refactor sırasında kararlılığı korumaya, hata olasılığını en aza indirmeye ve uzun vadeli bakım yapılabilirliği güçlendirmeye katkı sağlar
- Bu yaklaşım, “derlenmeyen bug en iyi bug'dır” ilkesini hayata geçirir
Henüz yorum yok.