Bir nesnenin var olduğunu düşünerek kod yazarsınız, uygulamayı çalıştırırsınız ve aniden meşhur “null reference” hatasıyla karşılaşırsınız. Rust, bu sürprizi çalışma zamanına bırakmak yerine bir değerin yokluğunu Option enum’u ile açıkça modellememizi ister. Böylece “Belki vardır, belki yoktur” ihtimali, derleyicinin denetleyebildiği güvenli bir tipe dönüşür.
``
Option neden gereklidir?
Java, C# ve JavaScript gibi dillerde bir referansın null olması mümkündür. Buna rağmen değişkenin tipi çoğu zaman bu ihtimali yeterince görünür kılmaz. Programcı kontrol yapmayı unutursa hata çalışma zamanında ortaya çıkar.
Rust’ta sıradan bir referans kendiliğinden null olamaz. Bir değerin bulunmaması geçerli bir durumsa standart kütüphanedeki şu enum kullanılır:
enum Option<T> {
Some(T),
None,
}
Buradaki T, saklanabilecek değerin tipidir. Örneğin Option<i32>, ya Some(i32) biçiminde bir tam sayı ya da None içerir. Olası durumların sayısını basitçe şöyle gösterebiliriz:
Formüldeki ekstra bir durum None seçeneğidir. Örneğin bool iki farklı değere sahipken Option<bool> üç durumu temsil eder: Some(true), Some(false) ve None.
| Yaklaşım | Değer varsa | Değer yoksa | Güvenlik |
|---|---|---|---|
| Null kullanılabilen referans | Nesne | null |
Kontrol unutulabilir |
Option<T> |
Some(T) |
None |
Derleyici iki durumu da ele aldırır |
| Varsayılan değer | Gerçek değer | Örneğin 0 |
Yokluk ile gerçek değer karışabilir |
Pattern matching ile değeri açmak
Option içindeki veriye doğrudan erişilemez. Önce hangi varyantla karşı karşıya olduğumuzu belirlememiz gerekir. En açık yöntem match ifadesidir:
fn kullanici_bul(id: u32) -> Option<String> {
if id == 42 {
Some(String::from("Ada"))
} else {
None
}
}
fn main() {
match kullanici_bul(42) {
Some(isim) => println!("Kullanıcı bulundu: {isim}"),
None => println!("Kullanıcı bulunamadı"),
}
}
Fonksiyon, sonuç bulmayı garanti etmediği için String yerine Option<String> döndürür. match, hem Some hem de None kolunu yazmamızı zorunlu tutar. Bir kol unutulursa program derlenmez; yani hata, kullanıcıya ulaşmadan yakalanır.
Yalnızca değer bulunduğunda işlem yapılacaksa if let daha kısa olabilir:
if let Some(isim) = kullanici_bul(42) {
println!("Hoş geldin, {isim}!");
}
Option yardımcı metotları
Rust, sık kullanılan senaryolar için pratik metotlar sunar:
| Metot | Davranış | Uygun kullanım |
|---|---|---|
unwrap_or(x) |
None ise x döndürür |
Güvenli varsayılan değer |
map(f) |
Mevcut değeri dönüştürür | Kısa veri işleme zinciri |
and_then(f) |
Yeni bir Option üreten işlem yapar |
Ardışık kontroller |
is_some() |
Değer varsa true döndürür |
Yalnızca varlık kontrolü |
unwrap() |
Değeri çıkarır, yoksa panikletir | Varlık kesin olarak kanıtlandıysa |
Örneğin yaş bilgisini güvenli biçimde dönüştürebiliriz:
fn yas_metni(yas: Option<u8>) -> String {
yas.map(|deger| format!("{deger} yaşında"))
.unwrap_or(String::from("Yaş bilinmiyor"))
}
map, yalnızca değer varsa closure’ı çalıştırır. Sonuç None kalırsa unwrap_or varsayılan metni üretir. Böylece elle dallanma yazmadan güvenli bir akış kurulur.
Option ile Result aynı şey değildir
Option, yalnızca değer bulunup bulunmadığını anlatır. Başarısızlığın nedenini de taşımak gerekiyorsa Result<T, E> daha uygundur.
fn bol(a: f64, b: f64) -> Option<f64> {
if b == 0.0 { None } else { Some(a / b) }
}
Bu örnekte yokluk sebebi zaten açıktır. Dosya okuma gibi bir işlemde ise “dosya yok”, “izin reddedildi” veya “disk hatası” ayrımı gerektiğinden Result seçilmelidir.
Kısacası Option, null değerini sihirli ve tehlikeli bir istisna olmaktan çıkarıp tip sisteminin parçası yapar. Rust size “Bu değer kesinlikle var mı?” sorusunu kod derlenmeden önce sordurur. Biraz disiplin karşılığında daha az gece yarısı hatası alınır; bu da oldukça kârlı bir takastır.
Yorumlar