---
title: 'Yokluğun Tiple İfadesi'
source: 'https://academia.sh/tr/kurslar/rust-soyutlama/yoklugun-tiple-ifadesi'
course: "Trait'ler, Genellikler ve Hata Yönetimi"
language: tr
updated: '2026-10-21T05:03:45+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Hazır ve tembel bir varsayılan gövdenin kaç kez çalıştığını koşturarak karşılaştırır'
    - 'Yokluk tipinin bir değerin bulunmama durumunu neden ayrı bir tip olarak taşıdığını açıklar'
    - 'Ucuz bir varsayılan gövdede iki yazımın aynı sonucu, farklı sayıyı verdiğini gösterir'
---

# Yokluğun Tiple İfadesi

Bir değerin bulunmayabileceği bir tipte taşınması ve o değeri karşılayacak yedek gövdenin — hazır mı tembel mi yazıldığına göre — değer zaten varken bile çalışıp çalışmadığının ölçülmesi.

İşleç Aşırı Yükleme dersi, bir işlecin her kullanımının aslında bir çağrı olduğunu ve
zincirlenmiş bir toplamda `islec-govdesi`nin iki kez çalıştığını ölçerek Trait ve
Genellikler konusunu kapattı. O ders, kaynakta yazılan bir gövdenin kullanım sayısı kadar
çalıştığını gösteriyordu — her kullanım kaçınılmaz bir koşumdu. Bu ders aynı soruyu bambaşka
bir yere taşıyor: bir gövde, yalnız *bir değer eksik olduğunda* çalışıyorsa, o gövde her
zaman mı çalışır, yoksa hiç çalışmayabilir mi?

Arayüzler, Hatalar ve Genellikler kursu (M09/K02) bir hatanın kendisinin bir dönüş değeri
olduğunu, kontrol akışının dışına çıkmadan taşınabileceğini kurmuştu; orada ölçülen, bir
hatanın *nerede* taşındığıydı — bildirimde değil, dönüş tipinde. Bu ders o kararı miras
alıyor, ama farklı bir eksenden bakıyor: bir değerin *yokluğunu* karşılayan gövde — yani
devreye giren yedek değer — kaç kez çalışıyor? Bu konunun (`error-handling`) beş dersi bu
soruyu sırayla yokluğa, başarısızlığa, kısa yazıma, dönüşüme ve kurtarılamaz duruma
uyguluyor; bu ilk ders yalnız yokluğa bakıyor, bir nedeni olan başarısızlık sıradaki
derste geliyor.

Trait'ler, Genellikler ve Hata Yönetimi kursu boyunca aynı soru hep aynı kalıyor: kaynakta
yazılan bir gövde, koşumda kaç kez çalışıyor? Trait ve Genellikler konusu bu soruyu bir
davranışın (trait) gerçekleştirimlerine sordu; bu konuda soru, bir işlemin sonucunu taşıyan
iki tipe — `Option` ve `Result` — ve onları izleyen kısa yazıma, dönüşüme ve kurtarılamaz
duruma soruluyor. Sorunun kendisi değişmiyor, yalnız uygulandığı yer değişiyor.

## Bir Tipte Yokluk

Rust'ta bir değerin bulunmayabileceği her yerde `Option<T>` kullanılır. Bu, önceki kursta
kurulan numaralandırma (enum) düzeneğinin standart kütüphanedeki bir örneğidir: iki
varyantı vardır, değeri taşıyan `Some(T)` ve değeri hiç taşımayan `None`. Bir işlevin
dönüş tipi `Option<i32>` ise, çağıran taraf derleme zamanında bu iki durumu da ele almak
zorunda kalır — istemeden `None` üzerinde bir değeri okumaya kalkmak, bu tipte yapısal
olarak mümkün değildir.

**HT1.** Bu, "değer yok" durumunun ayrı bir işaret değeriyle (örneğin sıfır ya da boş bir
dize) taşınmasından farklıdır. İşaret değeri, gerçek bir değerle karışabilir; `None` ise
`Some(T)`in taşıdığı `T` tipinden tamamen ayrı bir durumdur ve derleyici ikisini asla
karıştırmaz. Bu ders bu ayrımı yeniden kurmuyor — konu Sıralı ve Küme Yapıları dersinde
zaten değinilmişti — yalnız `Option`dan bir değer *çıkarmanın* kaç türlü yolu olduğuna ve
bu yolların kaynakta yazılan gövdeleri kaç kez çalıştırdığına bakıyor.

`Option` çoğunlukla elle kurulmaz; bir aramanın doğal sonucu olarak ortaya çıkar. Sıralı
bir sözlükte bir anahtarın karşılığı istendiğinde, anahtar oradaysa `Some(deger)`, değilse
`None` döner — arayan tarafın "anahtar var mı" diye ayrıca sormasına gerek kalmaz, arama
işleminin kendisi bu bilgiyi taşıyan bir değer üretir. Aynı düzen bir dizinin sınır dışı bir
indisi için de geçerlidir: doğrudan indisleme geçersiz bir indiste çöker, ama `get` çağrısı
bir `Option` döner ve çağıran taraf sınır dışı durumu sıradan bir `None` olarak ele alır.
Bu derste kullanılan örnekler bu aramaları taklit etmiyor; doğrudan `Some` ve `None`
değerleri kuruyor, çünkü ölçülen şey aramanın kendisi değil, sonucundan değer çıkarma
biçimidir.

Desen Eşleme dersi, bir `match` ifadesinin bütün dalları kapsaması gerektiğini —
bütünlük ilkesini — kurmuştu. Bir `Option` üzerinde elle yazılan bir `match` de bu ilkeye
uyar: `Some` ve `None` dallarının ikisi de yazılmalıdır, biri unutulursa kaynak hiç
derlenmez. `unwrap_or` ve `unwrap_or_else`, bu iki dallı `match`i tek bir yöntem çağrısında toplayan
kısayollardır — `Some(deger) => deger, None => yedek` yazımının kendisi kaynakta
yer almaz, ama derleyicinin ürettiği kod aynı iki dala ayrılır. Bu ders, o iki dalın kaç kez
çalıştığını, `match` yazımını tekrar kurmadan, doğrudan bu kısayolların üzerinden ölçüyor.

Aynı bütünlük ilkesi `if let` yazımı için de geçerli, ama farklı bir biçimde: `if let
Some(deger) = secenek { ... }`, yalnız `Some` dalını yazar ve `None` durumunu sessizce
atlar — bu, bütünlüğü çiğnemez, çünkü `if let` zaten bir `match`in *kısmi* bir biçimidir
ve dilin kendisi bunu böyle tanımlar. `unwrap_or` ve `unwrap_or_else` ise tam tersi bir
yerde durur: ikisi de her iki dalı da kapsar, farkları yalnız `None` dalının gövdesinin
kaynakta nasıl yazıldığındadır. Bu derste `if let` ölçülmüyor, çünkü onda "çalışmayan bir
dal" zaten kaynakta yok — ölçülen fark, iki dalı da yazan iki yöntemin birbirinden ne
kadar farklı çalıştığı.

## Hazır ve Tembel Varsayılan

Bir `Option` değerinden değer çekmenin en sık kullanılan iki yolu, değer yoksa devreye
giren bir yedek sağlamaktır. Bu yedek iki biçimde yazılabilir: doğrudan bir değer olarak
(`unwrap_or`) ya da bir kapanış (closure) döndüren bir çağrı olarak (`unwrap_or_else`).
İkisi de aynı işi görüyormuş gibi okunur, ama argümanlarının ne zaman değerlendirildiği
farklıdır.

Rust'ın Tasarım Hedefleri dersi (M10/K01) bir ifadenin değerlendirilip bir değer ürettiğini
kurmuştu; bir işlev çağrısının argümanı da kendi başına bir ifadedir ve çağrı gerçekleşmeden
önce değerlendirilir. `unwrap_or(hazir_varsayilan(&defter))` yazımı bu kuraldan muaf
değildir: `hazir_varsayilan(&defter)` bir argüman ifadesidir ve `unwrap_or`un kendisi
çalışmadan önce hesaplanır — `unwrap_or`un içinde `Some` mu `None` mi olduğuna bakan bir
dallanma varmış gibi görünse de, argüman bu dallanmadan önce çoktan değerlendirilmiştir.
`unwrap_or_else` ise argüman olarak bir *değer* değil, bir *kapanış* alır; kapanışın
kendisi kurulurken çalışmaz, yalnız `unwrap_or_else`in kendi iç uygulaması `None` dalına
girdiğinde çağrılır.

Aşağıdaki koşumda `dolu` ve `bos` elle kuruluyor, ama ikisi de bir aramanın döndürebileceği
iki durumu temsil ediyor: `dolu`, anahtarı bulunan bir aramayı; `bos`, bulunamayan bir
aramayı. Ölçülen gövde, `hazir_varsayilan` işlevi ve iki kapanış — üçü de sayaç defterine
bir kayıt düşerek kendi çalıştığını bildiriyor. Aşağıdaki koşum bu farkı doğrudan
gösteriyor.

```rust
// yoklukdefteri.rs — govde sayma cekirdegi: kere.rs'in Defter/say/kere kesiti
pub type Defter = std::rc::Rc<std::cell::RefCell<std::collections::BTreeMap<String, u32>>>;

pub fn say(defter: &Defter, ad: &str) {
    *defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}

pub fn kere(defter: &Defter, ad: &str) -> u32 {
    *defter.borrow().get(ad).unwrap_or(&0)
}
```

```rust
// yoklukolcumu.rs — hazir ve tembel varsayilanin kac kez costugu
mod yoklukdefteri;
use yoklukdefteri::{Defter, say, kere};
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::BTreeMap;

fn hazir_varsayilan(defter: &Defter) -> i32 {
    say(defter, "hazir-varsayilan");
    -1
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));

    let dolu: Option<i32> = Some(7);
    let bos: Option<i32> = None;

    let a = dolu.unwrap_or(hazir_varsayilan(&defter));
    let b = dolu.unwrap_or_else(|| {
        say(&defter, "tembel-varsayilan-gereksiz");
        -1
    });
    let c = bos.unwrap_or_else(|| {
        say(&defter, "tembel-varsayilan-gerekli");
        -1
    });

    println!("dolu.unwrap_or(...) sonucu: {}", a);
    println!("dolu.unwrap_or_else(...) sonucu: {}", b);
    println!("bos.unwrap_or_else(...) sonucu: {}", c);
    println!();
    for ad in ["hazir-varsayilan", "tembel-varsayilan-gereksiz", "tembel-varsayilan-gerekli"] {
        println!("{:<28}{} kez", ad, kere(&defter, ad));
    }
}
```

```
dolu.unwrap_or(...) sonucu: 7
dolu.unwrap_or_else(...) sonucu: 7
bos.unwrap_or_else(...) sonucu: -1

hazir-varsayilan            1 kez
tembel-varsayilan-gereksiz  0 kez
tembel-varsayilan-gerekli   1 kez
```

`dolu` doluyken bile `hazir_varsayilan` bir kez çalışıyor: `unwrap_or`un argümanı, çağrı
gerçekleşmeden önce sıradan bir işlev çağrısı olarak değerlendiriliyor. `Option`un `Some`
mu `None` mi olduğunun burada hiçbir önemi yok — argüman bir ifade olduğu için, kullanılıp
kullanılmayacağına bakılmaksızın hesaplanıyor. **HT2.** Bu yüzden `hazir-varsayilan` satırı
"her seferinde" sınıfına giriyor: `unwrap_or` her çağrıldığında, argümanı da kaçınılmaz
olarak çalışıyor.

`unwrap_or_else`e verilen kapanış ise tam tersi davranıyor. `dolu` üzerinde çağrıldığında
gövdesi hiç çalışmıyor — `tembel-varsayilan-gereksiz` sıfır kalıyor — çünkü kapanış yalnız
`None` durumunda çağrılıyor. `bos` üzerinde ise aynı kapanış deseni gerçekten çalışıyor ve
`tembel-varsayilan-gerekli` bire çıkıyor. Aradaki fark, argümanın türünde: biri hazır bir
değer, öbürü değeri *isteğe bağlı olarak üreten* bir çağrı. `tembel-varsayilan-gerekli`
"yalnız gerektiğinde" sınıfına giriyor — yokluk gerçekten oluştuğunda, gerektiği kadar
çalışıyor.

Arayüzler, Hatalar ve Genellikler kursunun (M09/K02) ölçtüğü şey bir hatanın *nerede*
bağlandığıydı: bildirimde mi, kullanım yerinde mi, değerin içinde mi. Bu ders o soruyu
sormuyor — `Option`un kendisi zaten M09/K02'nin üçüncü seçeneğiyle aynı aileden, bir
değerin içinde taşınan bir durum. Burada ölçülen, o durumu karşılayan gövdenin *kaç kez*
çalıştığı; `hazir-varsayilan` ile `tembel-varsayilan-gerekli` aynı sayıyı (bir) verse de,
biri koşulsuz çalışıyor, öbürü yalnız gerçekten gerektiğinde.

Yöntem adlarındaki `_or` ve `_or_else` ekleri bu ayrımı isimlendiriyor: `_or` eki, hazır
bir değerin doğrudan argüman olarak verildiği yöntemleri işaretliyor; `_or_else` eki, o
değerin yerine bir kapanışın verildiği yöntemleri. Bu adlandırma deseni `Option`a özgü
değil — standart kütüphane aynı `_or` / `_or_else` çiftini başka tiplerde de kullanıyor ve
her ikisinde de anlam aynı kalıyor: `_or` hazır, `_or_else` tembel. Sıradaki ders bu
adlandırma desenini `Result` üzerinde tekrar karşılayacak.

`Option` tipinin standart kütüphanedeki yöntemleri bu iki eksende bölünür. `unwrap_or`
gibi hazır bir değer isteyenler her zaman çalışır; `unwrap_or_else` gibi bir kapanış
isteyenler yalnız gerektiğinde çalışır. `and_then` gibi zincirleme bir yöntem yalnız
kapanış tarafında durur: verdiği kapanış yalnız `Some` durumunda çalışır, `None` üzerinde
hiç çağrılmaz. `map_or` ise ikisini aynı çağrıda birleştirir — ilk argümanı hazır bir
değerdir ve `None` durumunda bile her seferinde değerlendirilir, ikinci argümanı bir
kapanıştır ve yalnız `Some` durumunda çalışır; tek bir yöntem çağrısının içinde bu
dersin ölçtüğü iki sınıf bir arada görülebilir. Yöntem adı `_else` ekiyle bitmiyor diye
bu ayrımın geçerliliği bitmiyor; her yöntemin kendi belgesinde hangi argümanı ne zaman
çalıştırdığı, tam olarak bu derste ölçülen eksenle okunabilir.

## Sınırlayıcı Ölçüm

Yukarıdaki sayı farkı, tembel yazımın her zaman daha iyi bir seçim olduğu anlamına gelmez.
Fark yalnız *kaç kez çalıştığında*dır; gövde ucuzsa iki yazımın sonucu da birbirinden
ayırt edilemez.

```rust
// ucuzluk.rs — govde ucuzsa iki yazim da ayni sonucu verir, fark sayida kalir
mod yoklukdefteri;
use yoklukdefteri::{Defter, say, kere};
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::BTreeMap;

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let dolu: Option<i32> = Some(9);

    let a = dolu.unwrap_or({
        say(&defter, "ucuz-hazir");
        0
    });
    let b = dolu.unwrap_or_else(|| {
        say(&defter, "ucuz-tembel");
        0
    });

    println!("hazir yazimin sonucu: {}", a);
    println!("tembel yazimin sonucu: {}", b);
    println!("ucuz-hazir govdesi: {} kez", kere(&defter, "ucuz-hazir"));
    println!("ucuz-tembel govdesi: {} kez", kere(&defter, "ucuz-tembel"));
}
```

```
hazir yazimin sonucu: 9
tembel yazimin sonucu: 9
ucuz-hazir govdesi: 1 kez
ucuz-tembel govdesi: 0 kez
```

**HT3.** İki satır da aynı sonucu (`9`) veriyor; program hangi yazımın seçildiğinden
etkilenmiyor. Ayrışan tek şey, gövdenin çalışıp çalışmadığı: `ucuz-hazir` bir kez çalışıyor,
`ucuz-tembel` hiç çalışmıyor. Gövde burada yalnız bir tamsayı döndürdüğü için bu fark hiçbir
şeyi değiştirmiyor; ama gövde pahalı bir işse (bir dosya okuma, bir ağ çağrısı, büyük bir
hesap), aynı fark artık gözden kaçırılamayacak bir bedele dönüşür. Bu ders o bedeli
ölçmüyor — yalnız *kaç kez çalıştığını* sayıyor; bedelin büyüklüğü kaynakta yazılan
gövdenin kendisine bağlı ve bu ders onu değerlendirmiyor.

Bu ölçümün asıl sınırı burada görünüyor. Bir yazılım, bir ayar kaydında bulunmayan bir
alanın karşılığını sabit bir tam sayıyla dolduruyorsa, hazır ve tembel yazım arasında
gözle görülür bir fark yok — ikisi de aynı sayıyı üretiyor, aradaki tek fark yukarıdaki
koşumda görülen sayaç kaydı kadar küçük. Aynı yazılım, bulunmayan bir alanın karşılığını
bir dosyadan okuyarak ya da bir toplama hesaplayarak dolduruyorsa, hazır yazım o okumayı ya
da hesabı değer zaten varken bile çalıştırır — sonuç yine değişmez, çünkü `unwrap_or`
hesaplanan değeri zaten kullanmayacaktır, ama gövde yine de çalışmış olur. Bu ders hangi
gövdenin "ucuz" hangisinin "pahalı" sayılacağına karar vermiyor; yalnız iki yazımın kaç
kez çalıştığını sayıp bu kararı kaynak kodu okuyana bırakıyor. Sayı tek başına bir yazımın
diğerinden "daha iyi" olduğunu söylemiyor — yalnız neyin çalışıp neyin çalışmadığını
görünür kılıyor.

Yukarıdaki iki koşumda "hazır" taraf hep ayrı bir işlev olarak yazıldı, ama bu zorunlu
değil. `unwrap_or({ say(&defter, "ucuz-hazir"); 0 })` yazımındaki blok da kendi başına bir
ifadedir; ayrı bir işlev adı taşımadan da argüman olarak değerlendirilir. Ölçülen sınır bu
yüzden bir işlev çağrısına özgü değil — `unwrap_or`un argümanı ne biçimde yazılırsa
yazılsın, bir ifade olduğu sürece çağrılmadan önce değerlendirilir. Aradaki fark yalnız
okunabilirliktedir: ayrı bir işlev adı, gövdenin ne yaptığını isimlendirir; gömülü bir
blok, aynı işi adsız bırakır.

Bu üç koşum (hazır/tembel karşılaştırması, ucuz gövde karşılaştırması ve aradaki
isimlendirme farkı) birlikte tek bir sonuca varıyor: `Option`dan değer çıkarmanın hangi
yönteminin seçileceği, sonuçtan bağımsız bir sorudur. Sonuç her iki yazımda da aynı;
ayrışan yalnız kaynakta yazılan gövdenin çalışıp çalışmadığı ve kaç kez çalıştığı. Bu ayrım
küçük bir ayrıntı gibi görünse de, sıradaki dört ders boyunca aynı biçimde tekrar
karşılaşılacak: bir tipin hangi yöntemi seçildiğine göre, aynı kaynaktaki bir gövde bazen
kaçınılmaz olarak, bazen yalnız gerektiğinde, bazen de hiç çalışmıyor.

## Özet

- `Option<T>`, bir değerin bulunmayabileceği durumu ayrı bir tip olarak taşır; `Some(T)`
  ve `None` derleme zamanında ayrı ele alınır ve bir `match` bütünlük ilkesiyle ikisini de
  kapsamak zorundadır.
- `unwrap_or`a verilen argüman bir ifadedir ve değer zaten varken bile **her seferinde**
  çalışır — `Option`un hangi varyant olduğuna bakmaz.
- `unwrap_or_else`e verilen kapanış yalnız `None` durumunda çalışır: değer varken **hiç**
  çalışmaz, yokluk gerçekleştiğinde **yalnız gerektiğinde** çalışır.
- `map_or` gibi bazı yöntemler bu iki sınıfı aynı çağrıda birleştirir — bir argümanı her
  seferinde, öbürünü yalnız gerektiğinde çalışır.
- Gövde ucuzsa iki yazımın sonucu aynıdır; fark yalnız çalışma sayısındadır, gövde
  pahalılaştıkça bu fark önem kazanır.
- `_or` / `_or_else` adlandırma deseni hazır ve tembel ayrımını standart kütüphane
  genelinde işaretler; sıradaki derste `Result` üzerinde de görünür.

## Sonraki Adım

Bu ders, bir değerin *yok* olabileceği durumu ölçtü. Ama bir işlem başarısız olduğunda,
çoğu zaman yalnız "yok" demek yetmez — başarısızlığın *nedenini* de taşımak gerekir. `None`
bir nedeni yoktur, bir işlemin neden başarısız olduğunu söylemez; yalnız bir değerin
bulunmadığını söyler. Başarısızlığın Dönüş Değeri dersi aynı "kaç kez koştu" sorusunu,
artık bir nedeni de taşıyan bir tipe uyguluyor: başarı ve başarısızlık kolları ayrı
gövdeler olarak yazıldığında, aynı işlev arka arkaya birden çok kez çağrıldığında, bir
koşumda bu kollardan hangisi çalışıyor ve toplam çağrı sayısı sabit kalırken kol sayıları
nasıl ayrışıyor? Bu konunun adı (`error-handling`) o dersten itibaren gerçek anlamını
kazanıyor: artık ölçülen yalnız bir değerin yokluğu değil, adı konmuş bir başarısızlık.
