32 puan yazan GN⁺ 2025-12-07 | Henüz yorum yok. | WhatsApp'ta paylaş
  • 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.

Henüz yorum yok.