---
title: "Trait'ler, Genellikler ve Hata Yönetimi"
source: 'https://academia.sh/tr/kurslar/rust-soyutlama'
language: tr
updated: '2026-09-13T05:09:25+00:00'
license: 'CC BY-SA 4.0'
lessons: 15
outcomes:
    - 'Trait tanımlayarak davranış sözleşmesi kurar'
    - 'Bir gövdenin kaç kez koştuğunu ölçerek soyutlamanın bedelini görünür kılar'
    - 'Option ve Result ile hata yollarını tipte görünür kılar'
    - 'Yineleyici ve kapanışlarla bildirimsel dönüşüm yazar'
---

# Trait'ler, Genellikler ve Hata Yönetimi

Rust'ta bir soyutlamanın bedeli, kaynakta ne kadar yazdığında değil koşumda ne kadar çalıştığında görünüyor: kurs her düzeneği tek bir soruyla ölçer — kaynakta yazdığın bu gövde kaç kez koştu? Trait'ler, genellikler, hata yolları, kapanışlar ve yineleyici zincirleri aynı çerçeveden geçirilir.

## Trait ve Genellikler

Bir davranış sözleşmesinin tipten bağımsız tanımlanması, genel kodun gereksinimini sınırla bildirmesi, sözleşmenin kendi içinde bir tip taşıması, çağrının derleme zamanında mı koşumda mı çözüleceğinin seçilmesi, yazılmayan gövdelerin türetilerek üretilmesi, ve işleçlerin sözleşme üzerinden davranış kazanması.

### Trait Tanımı ve Gerçekleştirimi

Source: https://academia.sh/tr/kurslar/rust-soyutlama/trait-tanimi-ve-gerceklestirimi

Bir trait, tipten bağımsız bir davranış sözleşmesi kurar ve varsayılan bir gövde taşıyabilir; on altı gövdelik bir ölçüm çekirdeği koşturulduğunda bu varsayılan gövdenin bir tip için dört kez, onu yeniden yazan bir tip için sıfır kez koştuğu görülür — kurs boyunca sorulacak soru budur: kaynakta yazdığın bu gövde kaç kez koştu?

Sahiplik ve Bellek Modeli kursu bir değerin ömrünün nerede bittiğini ölçtü: kapsam sonunda mı,
taşındığı yerde mi, açık bir çağrıda mı, yoksa bir kabıyla birlikte mi. Bir değer ömrü boyunca
elinde kalırken, onunla **ne yapıldığı** ayrı bir sorudur ve bu kurs oraya bakıyor. Bir
davranışı bir tipten ayırmak — aynı işlevi birden fazla tipe aynı adla tanıtmak — o davranışın
gövdesinin **kaç kez çalıştığını** da belirleyen bir karar haline geliyor. Kurs boyunca tek bir
soru soruluyor: kaynakta yazdığın bu gövde kaç kez koştu?

Bu soruya üç yanıt var ve kurs boyunca bu üç ad değişmiyor.

#### Üç Sınıf

| Sınıf | Tanım |
|---|---|
| her seferinde | Çağrı ya da eleman başına kaçınılmaz olarak koşuyor |
| yalnız gerektiğinde | Yalnız sonucu istendiğinde, gerektiği kadar koşuyor |
| hiç | Kaynakta yazılı, ama bu koşumda bir kez bile koşmadı |

Bu üçlü daha önce sorulan iki soruyla karıştırılmamalı. Nesneye Dayalı Java kursu *kararı
hangi tipin verdiğini* ölçtü — bildirilen tip mi, çalışma zamanı tipi mi. Arayüzler, Hatalar
ve Genellikler kursu (Go) *bağın nerede kurulduğunu* ölçtü — bildirimde mi, kullanım yerinde
mi, değerin içinde mi. Bu kurs ikisini de sormuyor; **saymayı** soruyor. Bir trait
gerçekleştirimi yazıldığında bağ zaten kurulmuş olur — bu derste ölçülen, o bağ kurulduktan
sonra gövdenin çalışma zamanında kaç kez tetiklendiğidir.

Bir trait, bir davranış sözleşmesidir: bir ya da daha fazla yöntem imzası bildirir, bu
yöntemlerin tipten bağımsız bir adla çağrılabilmesini sağlar. Programlama Temelleri
kursundaki fonksiyon tanımı tek bir tipe bağlıydı; trait bunu genelliyor — aynı yöntem adı,
onu gerçekleştiren her tip için ayrı bir gövdeye karşılık gelebiliyor. Bu, arayüz (interface)
kavramına yakın görünse de aynı şey değil: arayüz, Go kursunda bağın *nerede* kurulduğu
ekseninde tanımlandı (örtük gerçekleştirim, bildirim gerektirmeden); trait ise Rust'ta bir
tipin `impl Ad for Tip` yazımıyla **açıkça** bildirdiği bir sözleşmedir ve üstelik varsayılan
bir gövde taşıyabilir. Kurs boyunca trait sözcüğü çevrilmeden kullanılacak.

#### Zorunlu ve İsteğe Bağlı Yöntemler

Bir trait bildirimi, `trait Ad { ... }` gövdesinin içinde bir dizi yöntem imzası sıralar.
Her imza iki biçimden birini alabilir: yalnız bir başlık (`fn ad(&self) -> Tip;`, noktalı
virgülle biten, gövdesiz) ya da bir başlık artı bir gövde (`fn ad(&self) -> Tip { ... }`).
Birinci biçim **zorunludur**: trait'i gerçekleştiren her tip bu yöntemi kendi `impl` bloğunda
mutlaka yazmak zorundadır, aksi halde gerçekleştirim tamamlanmamış sayılır ve kaynak
derlenmez. İkinci biçim **isteğe bağlıdır**: gerçekleştiren tip bu yöntemi hiç yazmayabilir,
yazmazsa trait'in kendi gövdesi devreye girer; yazarsa kendi gövdesi çalışır ve trait'inki
hiç görünmez.

Bu ders boyunca kullanılan `Etiket` trait'i üçünü birden gösteriyor. `defteri` ve `tip`
gövdesiz bildirilmiş — her gerçekleştirim bu ikisini kendi yazmak zorunda, çünkü trait'in
kendisi bir tipin defterine ya da adına dair hiçbir varsayımda bulunamaz. `ad` ise bir
varsayılan gövdeyle bildirilmiş: `Sade` bu yöntemi hiç yazmıyor ve trait'in gövdesini
miras alıyor, `Ozel` ise kendi gövdesini yazıp varsayılanı örtüyor. Aynı trait bildirimi
içinde zorunlu ve isteğe bağlı yöntemler bir arada durabiliyor; bu, bir sözleşmenin
kimi maddelerinin her imzalayan için farklı olmak zorunda olduğunu (adres, isim gibi), kimi
maddelerinin ise ortak bir varsayılanla başlayıp yalnız gerekince değiştirilebileceğini
söylemesine benziyor.

`impl Etiket for Sade` yazımının kendisi de iki parçaya ayrılıyor: `Etiket`, hangi
sözleşmenin gerçekleştirildiğini söylüyor; `Sade`, sözleşmeyi kimin gerçekleştirdiğini.
Aynı trait, farklı tipler için ayrı `impl` bloklarıyla defalarca gerçekleştirilebilir —
`kere.rs`de `Sade` ve `Ozel` için iki ayrı `impl Etiket for ...` bloğu bulunuyor, ikisi de
aynı trait adını taşıyor ama gerçekleştirdikleri gövdeler bağımsız.

#### Ölçüm Çekirdeği

Kursun geri kalanında kesitleri alınacak çekirdek `kere.rs`dir. On altı ayrı gövdeyi tek
koşumda sınıflandırır ve her birini bir gözlem defterine kaydeder. Defter, önceki kursun
paylaşımlı sahiplik dersinde kurulan düzeneği kullanıyor: `Rc<RefCell<BTreeMap<String, u32>>>`
— birden fazla sahibi olan, iç değişebilirlik taşıyan, sıralı bir sayaç.

```rust
// kere.rs — bir govde kac kez kostu: her seferinde mi, yalniz gerektiginde mi, hic mi.

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ops::Add;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

// --- trait: varsayilan govde ve onu yeniden yazan tip ---

trait Etiket {
    fn defteri(&self) -> &Defter;
    fn tip(&self) -> &'static str;
    fn ad(&self) -> String {
        let anahtar = format!("varsayilan-govde-{}", self.tip());
        say(self.defteri(), &anahtar);
        String::from("varsayilan")
    }
}

struct Sade {
    defter: Defter,
}
struct Ozel {
    defter: Defter,
}

impl Etiket for Sade {
    fn defteri(&self) -> &Defter {
        &self.defter
    }
    fn tip(&self) -> &'static str {
        "sade"
    }
}

impl Etiket for Ozel {
    fn defteri(&self) -> &Defter {
        &self.defter
    }
    fn tip(&self) -> &'static str {
        "ozel"
    }
    fn ad(&self) -> String {
        say(self.defteri(), "yeniden-yazilan-govde");
        String::from("ozel")
    }
}

// --- genellik ile trait nesnesi: ayni is, iki cagri bicimi ---

fn statik<T: Etiket>(x: &T) -> String {
    say(x.defteri(), "statik-gonderim");
    x.ad()
}

fn dinamik(x: &dyn Etiket) -> String {
    say(x.defteri(), "dinamik-gonderim");
    x.ad()
}

// --- islec asiri yukleme ---

#[derive(Clone)]
struct Uzunluk {
    deger: i32,
    defter: Defter,
}

impl Add for Uzunluk {
    type Output = Uzunluk;
    fn add(self, oteki: Uzunluk) -> Uzunluk {
        say(&self.defter, "islec-govdesi");
        Uzunluk {
            deger: self.deger + oteki.deger,
            defter: Rc::clone(&self.defter),
        }
    }
}

// --- hata yolu ---

#[derive(Debug)]
struct SayiHatasi;

impl From<std::num::ParseIntError> for SayiHatasi {
    fn from(_: std::num::ParseIntError) -> SayiHatasi {
        SayiHatasi
    }
}

fn cozumle(defter: &Defter, metin: &str, etiket: &str) -> Result<i32, SayiHatasi> {
    let sayi: i32 = metin.parse()?;
    say(defter, etiket);
    Ok(sayi * 2)
}

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

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let mut sinif: Vec<(&str, &str)> = Vec::new();

    // 1-2. varsayilan govde: yeniden yazmayan tipte kosuyor, yazan tipte hic kosmuyor
    let sade = Sade {
        defter: Rc::clone(&defter),
    };
    let ozel = Ozel {
        defter: Rc::clone(&defter),
    };
    let _ = sade.ad();
    let _ = ozel.ad();
    let _ = ozel.ad();

    // 3-4. ayni is iki cagri biciminde
    let _ = statik(&sade);
    let _ = statik(&sade);
    let _ = dinamik(&sade as &dyn Etiket);
    let _ = dinamik(&ozel as &dyn Etiket);

    // 5. islec govdesi
    let a = Uzunluk {
        deger: 3,
        defter: Rc::clone(&defter),
    };
    let b = Uzunluk {
        deger: 4,
        defter: Rc::clone(&defter),
    };
    let toplam = a.clone() + b;
    let toplam = toplam + a;
    let _ = toplam.deger;

    // 6. hazir varsayilan: deger VARKEN bile kosuyor
    let dolu: Option<i32> = Some(7);
    let _ = dolu.unwrap_or(hazir_varsayilan(&defter));

    // 7-8. tembel varsayilan: deger varken hic, yokken bir kez
    let bos: Option<i32> = None;
    let _ = dolu.unwrap_or_else(|| {
        say(&defter, "tembel-varsayilan-gereksiz");
        -1
    });
    let _ = bos.unwrap_or_else(|| {
        say(&defter, "tembel-varsayilan-gerekli");
        -1
    });

    // 9-10. soru isaretinden sonrasi
    let _ = cozumle(&defter, "21", "soru-sonrasi-basarili");
    let _ = cozumle(&defter, "yirmibir", "soru-sonrasi-basarisiz");

    // 11. hata donusumu
    if cozumle(&defter, "yirmibir", "yok-sayilan").is_err() {
        say(&defter, "hata-donusumu");
    }

    // 12. tuketilmeyen zincir
    let _tembel = vec![1, 2, 3, 4].into_iter().map(|x| {
        say(&defter, "tuketilmeyen-zincir");
        x * 2
    });

    // 13. tuketilen zincir: her elemanda
    let toplam: i32 = vec![1, 2, 3, 4]
        .into_iter()
        .map(|x| {
            say(&defter, "tuketilen-zincir");
            x * 2
        })
        .sum();
    let _ = toplam;

    // 14. erken biten zincir: yalniz gerektigi kadar
    let bulunan = vec![1, 2, 3, 4]
        .into_iter()
        .map(|x| {
            say(&defter, "erken-biten-zincir");
            x * 2
        })
        .find(|x| *x > 2);
    let _ = bulunan;

    // 15. cagrilmayan kapanis
    let _kapanis = || say(&defter, "cagrilmayan-kapanis");

    // --- rapor ---
    let govdeler = [
        ("varsayilan-govde-sade", "her seferinde"),
        ("yeniden-yazilan-govde", "her seferinde"),
        ("statik-gonderim", "her seferinde"),
        ("dinamik-gonderim", "her seferinde"),
        ("islec-govdesi", "her seferinde"),
        ("hazir-varsayilan", "her seferinde"),
        ("tuketilen-zincir", "her seferinde"),
        ("hata-donusumu", "her seferinde"),
        ("tembel-varsayilan-gerekli", "yalniz gerektiginde"),
        ("soru-sonrasi-basarili", "yalniz gerektiginde"),
        ("erken-biten-zincir", "yalniz gerektiginde"),
        ("varsayilan-govde-ozel", "hic"),
        ("tembel-varsayilan-gereksiz", "hic"),
        ("soru-sonrasi-basarisiz", "hic"),
        ("tuketilmeyen-zincir", "hic"),
        ("cagrilmayan-kapanis", "hic"),
    ];

    println!("{:<28}{:<22}{}", "govde", "sinif", "kac kez");
    for (ad, sinifi) in govdeler {
        let n = kere(&defter, ad);
        println!("{:<28}{:<22}{}", ad, sinifi, n);
        sinif.push((ad, sinifi));
    }

    println!();
    for s in ["her seferinde", "yalniz gerektiginde", "hic"] {
        let adet = sinif.iter().filter(|(_, k)| *k == s).count();
        println!("{:<22}{}", s, adet);
    }
    println!("toplam govde: {}", sinif.len());
}
```

```
govde                       sinif                 kac kez
varsayilan-govde-sade       her seferinde         4
yeniden-yazilan-govde       her seferinde         3
statik-gonderim             her seferinde         2
dinamik-gonderim            her seferinde         2
islec-govdesi               her seferinde         2
hazir-varsayilan            her seferinde         1
tuketilen-zincir            her seferinde         4
hata-donusumu               her seferinde         1
tembel-varsayilan-gerekli   yalniz gerektiginde   1
soru-sonrasi-basarili       yalniz gerektiginde   1
erken-biten-zincir          yalniz gerektiginde   2
varsayilan-govde-ozel       hic                   0
tembel-varsayilan-gereksiz  hic                   0
soru-sonrasi-basarisiz      hic                   0
tuketilmeyen-zincir         hic                   0
cagrilmayan-kapanis         hic                   0

her seferinde         8
yalniz gerektiginde   3
hic                   5
toplam govde: 16
```

**TR1.** Bu koşum belirlenimcidir: beş ayrı koşumda kaynağın aynı on altı satırı, aynı sırayla
ve aynı sınıf dağılımıyla üretildiği doğrulandı. **On altı gövdenin 8'i her seferinde, 3'ü
yalnız gerektiğinde koştu, 5'i hiç koşmadı.** Bu sayı kursun geri kalanında değişmiyor; her
ders yalnız kendi kesitini alıyor. Rapor bir sıralı sözlük (`BTreeMap`) kullanıyor — bir karma
sözlüğün gezinme sırası koşumdan koşuma değişebileceği için, sayım burada bile sıralamayı
güvenceye bağlı bir yapıya bırakıyor.

#### Varsayılan Gövde İki Tip İçin İki Ayrı Sonuç Veriyor

`Etiket` trait'i üç yöntem bildiriyor: `defteri` ve `tip` hiç gövde taşımıyor — her
gerçekleştirim bunları kendisi yazmak zorunda. Üçüncü yöntem, `ad`, bir **varsayılan gövde**
taşıyor: `impl Etiket for Sade` bu gövdeye hiç dokunmadığı için, `Sade` üzerinde `ad`
çağrıldığında trait'in kendi gövdesi çalışıyor. `Ozel` ise `ad`'ı kendi gövdesiyle **yeniden
yazıyor** — trait'teki varsayılan gövde `Ozel` için hiçbir zaman devreye girmiyor.

Tablodaki iki satır bu ikiliyi doğrudan gösteriyor. `varsayilan-govde-sade` dört kez koştu:
`sade.ad()` bir kez doğrudan çağrıldı, `statik(&sade)` iki kez çağrıldı ve her çağrı kendi
içinde `x.ad()`'ı tetikledi, `dinamik(&sade ...)` bir kez çağrıldı ve o da aynı şekilde
`x.ad()`'ı tetikledi — toplam dört çağrı, dördü de trait'in varsayılan gövdesine düştü.
`yeniden-yazilan-govde` ise üç kez koştu: `ozel.ad()` iki kez doğrudan, `dinamik(&ozel ...)`
bir kez — üçü de `Ozel`in kendi yazdığı gövdeyi çalıştırdı, trait'in varsayılanına hiç
uğramadı. İkisi de **her seferinde** sınıfına giriyor: çağrıldıkları her yerde kaçınılmaz
olarak koşuyorlar, aradaki fark hangi gövdenin koştuğunda.

**TR2.** Arayüzler, Hatalar ve Genellikler kursu bir gerçekleştirimin **bağının nerede
kurulduğunu** ölçtü — Go'da bir tip bir arayüzü örtük olarak, bildirim yapmadan
gerçekleştiriyordu. Rust'ta bu bağ `impl Etiket for Sade` satırıyla **açıkça** kuruluyor;
kurs bu farkı bir kez kaydedip geçiyor. Burada asıl ölçülen, bağ kurulduktan **sonra** o
gerçekleştirimin gövdesinin koşum sırasında kaç kez tetiklendiği — `varsayilan-govde-sade`
ile `yeniden-yazilan-govde` arasındaki 4 ile 3 farkı, bağın kendisinden değil çağrı
sayısından geliyor.

`hazir-varsayilan`, `islec-govdesi` ve hata yolundaki `hata-donusumu` de aynı sınıfa giriyor —
her seferinde koşuyorlar, çünkü çekirdekteki her biri tam olarak çağrıldığı kadar çalışıyor.
Kursun ilerleyen dersleri bu satırların her birini ayrı ayrı büyütecek: Trait Sınırları
`statik-gonderim`in sınıra bağlı olup olmadığını, İlişkili Tipler `islec-govdesi`nin
imzasındaki `Output` alanını, Nesne Güvenliği ve Dinamik Gönderim `statik-gonderim` ile
`dinamik-gonderim`i yan yana koyacak.

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

`varsayilan-govde-ozel` satırı sıfır. Bu, varsayılan gövdenin **var olmadığı** anlamına
gelmiyor — kaynakta duruyor, `trait Etiket` bildiriminin içinde, tıpkı `Sade` için koştuğu
haliyle. Sıfır olmasının nedeni, `Ozel`in kendi `ad` yöntemini yazarak bu gövdeye hiç
uğramaması. Aynı kaynak satırı bir tip için dört kez çalışırken başka bir tip için hiç
çalışmıyor; koşum sayısı gövdenin varlığını değil, hangi tiplerin bu gövdeyi **kullandığını**
ölçüyor.

Bu ayrım kursun geri kalanında tekrar karşımıza çıkacak: `hiç` sınıfına giren beş gövdenin
hiçbiri kaynaktan silinmiş değil, beşi de bu koşumda hiç tetiklenmemiş. Bir sayının sıfır
olması, "bu kod gereksiz" demek değil; "bu koşumda bu yol seçilmedi" demek.

Varsayılan gövdenin kendisi de bunu görünür kılacak biçimde yazıldı: anahtar dizgesi
`self.tip()` çağrısının döndürdüğü değer bir kalıba yerleştirilerek kuruluyor, sabit bir
dizgeyle değil. Trait'in kendi gövdesi, kendisini çalıştıran tipin adını hiç
bilmiyor — `self.tip()` bir dize üretiyor ve gövde onu ödünç alıp anahtara ekliyor. Bu, tek
bir varsayılan gövdenin, onu miras alan her tip için ayrı bir satırda sayılabilmesini
sağlıyor; `Sade` ve `Ozel` aynı varsayılan gövdeyi paylaşsaydı bile (bu örnekte paylaşmıyorlar,
çünkü `Ozel` kendi gövdesini yazdı) iki ayrı anahtar altında ayrı ayrı sayılabilirlerdi. Bu
küçük tasarım kararı, çekirdeğin geri kalan derslerde neden kesit alınabilir olduğunu da
açıklıyor: her gövde kendi anahtarını taşıyor, bir kesit alındığında geri kalan anahtarlar
etkilenmiyor.

#### Özet

- Trait, tipten bağımsız bir davranış sözleşmesi kurar ve gerçekleştiren tip yazmazsa
  devreye giren bir varsayılan gövde taşıyabilir.
- Ölçüm çekirdeği on altı gövdeyi sınıflandırıyor: 8'i her seferinde, 3'ü yalnız
  gerektiğinde, 5'i hiç koşuyor; bu sayı kurs boyunca değişmiyor.
- Aynı varsayılan gövde bir tip için dört kez (her seferinde) koşarken, onu yeniden yazan
  bir tip için sıfır kez (hiç) koşuyor — gövde kaynakta aynı, koşum sayısı farklı.
- trait ile arayüz ayrı kavramlardır: arayüz Go'da bağın nerede kurulduğu ekseninde
  tanımlandı, trait burada açık bildirimle kurulan ve varsayılan gövde taşıyabilen bir
  sözleşmedir.
- Sıfır koşum, gövdenin kaynakta yok olduğu anlamına gelmez; o tip için o yola hiç
  uğranmadığı anlamına gelir.

#### Sonraki Adım

Trait'i gerçekleştiren bir tipin gövdesi ölçülebildi, ama bir gövdeyi genel bir fonksiyonun
içinde kullanabilmek için o fonksiyonun tip parametresine bir **sınır** koymak gerekiyor.
Sıradaki ders bu sınırın koşum sayısına hiç dokunup dokunmadığını sorar — aynı gövde sınırlı
ve sınırsız iki biçimde yazılıp koşturularak.

### Trait Sınırları

Source: https://academia.sh/tr/kurslar/rust-soyutlama/trait-sinirlari

Bir genel fonksiyonun tip parametresine bir trait sınırı koymak, kaynağa hiç adım eklemez — aynı gövde sınırlı ve sınırsız iki biçimde yazılıp koşturulduğunda ikisi de birebir aynı sayıyı verir; sınırın bedeli koşum defterine değil derlemeye yazılır ve bu sayıyla ölçülemez.

Önceki ders bir trait'in varsayılan gövde taşıyabildiğini ve bu gövdenin hangi tip için
çalıştığının koşumda sayılabildiğini gösterdi. Ama `statik` gibi bir fonksiyonun `<T: Etiket>`
yazımı henüz açıklanmadı — bu, bir genel fonksiyonun tip parametresine konan bir **trait
sınırı**dır ve bu dersin konusu bu sınırın kendisi. Soru aynı kalıyor: kaynakta yazdığın bu
gövde kaç kez koştu? Ama bu kez ölçülen gövde değil, gövdenin etrafına konan **koşul**.

#### Sınır Ne Yazıyor

Bir genel fonksiyon, `fn ad<T>(x: &T)` yazımıyla herhangi bir tipi kabul edebilir; ama gövde
içinde `x` üzerinde bir yöntem çağırmak istiyorsa, derleyicinin o yöntemin var olduğunu
bilmesi gerekir. Trait sınırı tam bunu sağlıyor: `fn ad<T: Etiket>(x: &T)` yazımı, "`T` ne
olursa olsun, `Etiket`i gerçekleştirmiş olmalı" diyor. Sınır konmadan `x.defteri()` çağrısı
derlenmez, çünkü derleyici `T` için hangi yöntemlerin var olduğunu bilmiyor; sınır konduktan
sonra derleyici bu bilgiyi trait bildiriminden okuyor.

Sınırsız bir genel fonksiyon, `T` hakkında hiçbir şey bilmiyor demektir. `fn ad<T>(x: &T)`
yazıldığında derleyici `x`e yalnız her tipte ortak olan işlemleri (taşımak, bir referans
almak) uygulamaya izin verir; `x.defteri()` gibi `Etiket`e özgü bir çağrı yazılırsa derleyici
bunu reddeder, çünkü elindeki tek bilgi `T`nin *bir tip olduğu*dur, `Etiket`i
gerçekleştirdiği değil. Bu redd, önceki kurstaki tip anahtarının aksine burada bir çalışma
zamanı davranışı değil — kaynak hiç derlenmiyor. Trait sınırı tam bu boşluğu dolduruyor:
`T: Etiket` yazıldığı andan itibaren derleyici, gövdenin içinde `Etiket`in bildirdiği her
yöntemin `x` üzerinde çağrılabileceğini biliyor ve `x.defteri()` derlenir hale geliyor. Sınır
böylece iki tarafı aynı anda bağlıyor: çağıran tarafı ("bu fonksiyona yalnız `Etiket`i
gerçekleştiren tipler geçirilebilir") ve gövde tarafını ("bu gövde `Etiket`in yöntemlerini
güvenle çağırabilir").

Aynı sınır iki ayrı yerde yazılabilir. Kısa biçim tip parametresinin hemen yanına gelir:
`fn sinirli_govde<T: Etiket>(x: &T)`. Sınır uzadığında (birden fazla trait, uzun tip adları)
aynı anlam bir `where` yan tümcesiyle de yazılabilir:

```rust
// where-bicimi.rs — ayni sinir, imzadan ayrilmis bicimde.
trait Etiket {
    fn ad(&self) -> String;
}

struct Ornek;
impl Etiket for Ornek {
    fn ad(&self) -> String {
        String::from("ornek")
    }
}

fn kisa<T: Etiket>(x: &T) -> String {
    x.ad()
}

fn uzun<T>(x: &T) -> String
where
    T: Etiket,
{
    x.ad()
}

fn main() {
    let o = Ornek;
    println!("kisa={} uzun={}", kisa(&o), uzun(&o));
}
```

```
kisa=ornek uzun=ornek
```

**TR3.** `kisa` ve `uzun` derleyici için tamamen aynı anlama geliyor; `where` yan tümcesi bir
okunabilirlik seçeneği, ayrı bir mekanizma değil. Birden fazla sınır gerektiğinde
(`T: Etiket + Clone` gibi) kısa biçim satırı uzatırken, `where` biçimi her sınırı ayrı bir
satıra koyarak imzayı kısa tutabiliyor. Bu ders ikisini birbirinin yerine kullanıyor;
hangisinin seçildiği koşum sayısını hiç etkilemiyor.

Birden fazla sınır `+` işaretiyle birleştirilir: `T: Etiket + Clone`, "`T` hem `Etiket`i
gerçekleştirmeli hem de `Clone`u" demektir. Bu, önceki derste tanıdık bir yapı: `Etiket`in
kendisi tek bir sözleşmeydi, burada iki sözleşme aynı tip parametresine birden bağlanıyor.
`+` burada bir birleşim değil bir **kesişim** kuruyor: `T` yerine geçebilecek tipler kümesi,
yalnız `Etiket` gerçekleştirenlerin kümesiyle yalnız `Clone` gerçekleştirenlerin kümesinin
kesişimidir. Bir sınır eklemek, hangi tiplerin fonksiyona geçirilebileceğini her zaman
**daraltır**, hiçbir zaman genişletmez; bu yüzden bir imzaya yeni bir sınır eklemek geriye
dönük olarak bazı çağrıları kesebilir, ama hiçbir zaman yeni bir çağrıyı mümkün kılmaz.

Sınırlar, tip parametresinin *nerede* kısıtlandığını da değiştirmiyor: `<T: Etiket>` ile
`where T: Etiket` arasındaki tek fark yerleşimdir, kısıtın gücü değil. İki sınır bir arada
yazıldığında (`T: Etiket + Clone`) da aynı ilke geçerli: derleyici gövdeyi kontrol ederken
`T`nin *hem* `Etiket`in *hem* `Clone`un yöntemlerini taşıdığını varsayıyor; sınırlardan
biri eksik olsaydı, o trait'e ait tek bir çağrı bile gövdeyi derlenemez hale getirirdi.

Bir imza birden fazla tip parametresi taşıyabilir ve her parametrenin kendi sınırı olabilir:
`fn birlestir<T: Etiket, U: Etiket>(a: &T, b: &U)` yazımında `T` ile `U` aynı sınırı
paylaşıyor görünse de derleyici için bağımsız iki parametredir — `a` ile `b` farklı somut
tiplerde olabilir, tek ortak zorunluluk ikisinin de `Etiket`i gerçekleştirmiş olmasıdır.
Sınırlar parametre başına konur; bir parametrenin sınırı diğerini etkilemez, aynı trait adı
iki yerde tekrarlansa bile.

```rust
// birlestir.rs — iki tip parametresi, ayni trait sinirini bagimsiz tasiyor.
trait Etiket {
    fn ad(&self) -> String;
}

struct Sade;
struct Ozel;

impl Etiket for Sade {
    fn ad(&self) -> String {
        String::from("sade")
    }
}
impl Etiket for Ozel {
    fn ad(&self) -> String {
        String::from("ozel")
    }
}

fn birlestir<T: Etiket, U: Etiket>(a: &T, b: &U) -> String {
    format!("{}+{}", a.ad(), b.ad())
}

fn main() {
    let s = Sade;
    let o = Ozel;
    println!("{}", birlestir(&s, &o));
    println!("{}", birlestir(&s, &s));
}
```

```
sade+ozel
sade+sade
```

İkinci çağrıda `T` ve `U` aynı somut tipe (`Sade`) çözülüyor; ilkinde ise ikisi farklı
somut tiplere (`Sade` ve `Ozel`) çözülüyor. `birlestir`in imzası her iki çağrıyı da kabul
ediyor, çünkü sınır yalnız "`Etiket`i gerçekleştirmiş olmalı" diyor, "aynı tip olmalı"
demiyor. `T` ile `U`nun aynı somut tipe çözülmesini zorunlu kılmak isteseydik imza tek bir
parametre kullanırdı (`fn birlestir<T: Etiket>(a: &T, b: &T)`); iki ayrı parametrenin
varlığı, tam olarak bu zorunluluğu **kaldırmak** için orada duruyor.

Trait sınırı yazılmadığında tip parametresi tamamen boş kalmıyor: her `T`, aksi
belirtilmedikçe örtük bir **boyut** sınırı da taşır — derleyici, `T`nin bellekte sabit ve
bilinen bir boyutu olduğunu varsayar, çünkü genel bir gövde `T` tipinde bir değeri
yerelde tutabilmelidir. Bu örtük sınır, `Etiket` gibi açıkça yazılan sınırlardan ayrı bir
katmandır ve çoğu genel fonksiyon için hiç görünmez; yalnız dilim gibi boyutu değişken
tiplerle çalışan az sayıda ileri düzey yazımda açıkça gevşetilmesi gerekir. Bu ders o
gevşetmeyi kullanmıyor — burada önemli olan, sınırın **görünen** kısmının (`Etiket`) yanında
her zaman görünmeyen bir varsayılan sınırın da bulunduğunu bilmek.

#### Sınırın Kosumdaki Payı: Sıfır Adım

Aynı işi iki yazımla kurup koşum sayısını karşılaştıralım: biri `<T: Etiket>` sınırıyla genel,
öbürü doğrudan somut tip (`Sade`) alan, sınırsız bir fonksiyon. Üçüncü bir fonksiyon da
birden fazla sınırı (`Etiket + Clone`) birlikte kullanıyor.

```rust
// kere.rs — trait Etiket'in kesiti: `defteri`, tip adi ve iki gerceklestirim.

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

pub type Defter = Rc<RefCell<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)
}

pub trait Etiket {
    fn defteri(&self) -> &Defter;
    fn tip(&self) -> &'static str;
}

#[derive(Clone)]
pub struct Sade {
    pub defter: Defter,
}

impl Etiket for Sade {
    fn defteri(&self) -> &Defter {
        &self.defter
    }
    fn tip(&self) -> &'static str {
        "sade"
    }
}

pub struct Ozel {
    pub defter: Defter,
}

impl Etiket for Ozel {
    fn defteri(&self) -> &Defter {
        &self.defter
    }
    fn tip(&self) -> &'static str {
        "ozel"
    }
}
```

```rust
// sinir-govdesi.rs — trait sinirinin kosumda hicbir adim eklemedigini olcer.
mod kere;
use kere::{kere, say, Defter, Etiket, Ozel, Sade};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

// Ayni kaynak satiri, cagrildigi her somut tip icin derleyici tarafindan
// ayrica uretiliyor - bu, kosum sayacinin gormedigi bir bedeldir.
fn sinirli_govde<T: Etiket>(x: &T) -> u32 {
    let anahtar = format!("sinirli-govde-{}", x.tip());
    say(x.defteri(), &anahtar);
    kere(x.defteri(), &anahtar)
}

// Ayni is, sinirsiz: dogrudan somut tip aliyor, tip parametresi yok.
fn somut_govde(x: &Sade) -> u32 {
    say(x.defteri(), "somut-govde");
    kere(x.defteri(), "somut-govde")
}

// T hem Etiket'i hem Clone'u sagliyor olmali - iki sinir '+' ile birlesiyor.
fn kopyala_ve_say<T: Etiket + Clone>(x: &T) -> T {
    say(x.defteri(), "coklu-sinir-govdesi");
    x.clone()
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let sade = Sade { defter: Rc::clone(&defter) };
    let ozel = Ozel { defter: Rc::clone(&defter) };

    let _ = sinirli_govde(&sade);
    let _ = sinirli_govde(&sade);
    let _ = sinirli_govde(&ozel);

    let _ = somut_govde(&sade);
    let _ = somut_govde(&sade);

    let kopya = kopyala_ve_say(&sade);
    let _ = kopyala_ve_say(&kopya);

    let mut govdeler: BTreeMap<&str, u32> = BTreeMap::new();
    govdeler.insert("sinirli-govde-sade", kere(&defter, "sinirli-govde-sade"));
    govdeler.insert("sinirli-govde-ozel", kere(&defter, "sinirli-govde-ozel"));
    govdeler.insert("somut-govde", kere(&defter, "somut-govde"));
    govdeler.insert("coklu-sinir-govdesi", kere(&defter, "coklu-sinir-govdesi"));

    println!("{:<22}{}", "govde", "kac kez");
    for (ad, n) in &govdeler {
        println!("{:<22}{}", ad, n);
    }
}
```

```
govde                 kac kez
coklu-sinir-govdesi   2
sinirli-govde-ozel    1
sinirli-govde-sade    2
somut-govde           2
```

**TR4.** `sinirli-govde-sade` iki kez koştu, `somut-govde` de iki kez — birebir aynı sayı.
İkisi de aynı işi (defteri okuyup bir satırı saymak) yapıyor; biri genel bir tip parametresi
üzerinden, öbürü doğrudan somut tip üzerinden. Trait sınırı, gövdenin **kaç kez** çağrıldığına
hiçbir şey eklemiyor ya da çıkarmıyor — bu, sınırın koşum defterinde hiçbir iz bırakmadığını
gösteriyor. Sınır bir **derleme zamanı** kuralıdır: hangi tiplerin `sinirli_govde`ye
geçirilebileceğini belirler, ama geçirildikten sonra çalışan kod, sınır hiç yokmuş gibi
davranır.

Arayüzler, Hatalar ve Genellikler kursu Go'nun genelliklerinde **bağın kullanım yerinde
kurulduğunu** ölçmüştü — bir genel fonksiyonun tip kısıtı, çağrının hangi tiplerle
yapılabileceğini kullanım yerinde sınırlıyordu. Bu kurs aynı soruyu sormuyor; burada ölçülen,
o sınırın **koşuma ne eklediği** — yanıt hiçbir şey. `sinirli-govde-ozel` satırı bunu ikinci
bir açıdan doğruluyor: aynı `sinirli_govde` fonksiyonu `Ozel` ile de çağrılabiliyor (bir kez),
çünkü `Ozel` de `Etiket`i gerçekleştiriyor; sınır, hangi tiplerin kabul edileceğini
belirliyor, ama kabul edilen her tip için gövde aynı biçimde çalışıyor.

`coklu-sinir-govdesi` de aynı sınıfa giriyor: iki kez çağrıldı, iki kez koştu. Burada
`T: Etiket + Clone` sınırı iki ayrı koşulu birleştiriyor, ama gövdenin kendisi yine tek bir
sayma çağrısı ve bir kopyalama işlemi. Sınırın sayısı arttıkça (`Etiket`, sonra
`Etiket + Clone`) koşum defterindeki satır sayısı artmıyor; sınır tip parametresine konan bir
koşuldur, gövdenin içine eklenen bir adım değildir.

Dört satırın tamamı **her seferinde** sınıfına giriyor: hiçbiri "yalnız gerektiğinde" ya da
"hiç" değil, çünkü her çağrı kaçınılmaz olarak karşılığını buluyor — atlanan, ertelenen ya da
tüketilmeyen bir adım yok. Bu, önceki dersteki `varsayilan-govde-sade` ve
`yeniden-yazilan-govde` satırlarıyla aynı sınıf: farklı olan yalnız gövdenin hangi koşulla
çağrılabildiği, gövdenin kendisinin çalışıp çalışmadığı değil.

Bu eşitlik, kursun ilerleyen bir dersinde farklı bir soruyla yeniden karşımıza çıkacak. Nesne
Güvenliği ve Dinamik Gönderim dersi, aynı `Etiket` sözleşmesini bu kez `<T: Etiket>` sınırı
yerine `&dyn Etiket` ile kullanan bir çağrı biçimiyle karşılaştıracak. Orada da sayı eşit
çıkıyor — ama nedeni burada görülenden farklı: sınırlı genellik çağrıyı derleme zamanında
çözerken, `dyn` biçimi aynı çağrıyı koşum sırasında bir yöntem tablosundan buluyor. Bu ders
yalnız sınırın **varlığının** koşuma bir şey eklemediğini gösteriyor; iki çağrı biçiminin
birbirinden nasıl ayrıldığı ayrı bir derste ele alınacak.

`somut_govde` fonksiyonunun hiç sınıra ihtiyacı olmaması da aynı ilkenin bir sonucu. Bir
fonksiyon zaten tek bir somut tipe (`Sade`) bağlandığında "hangi tipler kabul edilsin"
sorusunun yanıtı baştan bellidir — yalnız `Sade`. Trait sınırı, bu sorunun yanıtının
**birden fazla** tip olabildiği durumlar için var; tek tipe bağlı bir fonksiyonda sormaya
gerek bile yok. Bu yüzden `somut_govde` ile `sinirli_govde` arasındaki fark bir eksiklik
değil, iki farklı sorunun karşılığı: biri "bu iş için doğru tip hangisi" sorusunu hiç
sormuyor, öbürü bu soruyu sınırla yanıtlıyor.

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

Koşumda bedeli olmaması, sınırın **bedelsiz** olduğu anlamına gelmiyor. `sinirli_govde`
fonksiyonu `Sade` ve `Ozel` için ayrı ayrı çağrıldığında, derleyici bu fonksiyonun **her somut
tip için ayrı bir gövde** ürettiği bir yöntemle çalışıyor: kaynakta tek bir `fn
sinirli_govde<T: Etiket>` satırı var, ama üretilen ikili dosyada `Sade` için bir gövde,
`Ozel` için başka bir gövde bulunuyor. Bu üretim derleme sırasında olur ve koşum defterine
hiç girmez — defter yalnız çalışma zamanındaki çağrıları sayıyor, derleme zamanında üretilen
gövde sayısını değil. Bu kursun ölçüm yöntemi bu bedeli **gösteremiyor**; ders bunu açıkça
söylüyor ve saymaya kalkmıyor. `sinirli-govde-sade` ile `sinirli-govde-ozel`in toplamda üç
kez koşması, arkada kaç ayrı gövdenin üretildiğiyle ilgili hiçbir bilgi taşımıyor.

Bu sınır, kursun ölçüm yönteminin genel bir sınırını da ortaya koyuyor: koşum defteri yalnız
**çalışma zamanında gerçekten geçilen** satırları sayabiliyor. Derleme zamanında olan
hiçbir şey — kaç ayrı gövde üretildiği, hangi tipin hangi sırayla derlendiği, bir sınırın
kaç aday tipi elediği — bu defterle görünür kılınamaz. Kurs boyunca "sayı" sözcüğü hep
**çalışma zamanı sayısı** anlamına geliyor; derleme zamanına ait bir soru sorulduğunda ders
bunu açıkça söyleyip düzyazıyla yanıtlıyor, bir sayı uydurmuyor. Bu ayrım, ölçümün neyi
kapsayıp neyi kapsamadığını baştan çizmek için gerekli: kursun geri kalanında "bu satır
hiç koşmadı" cümlesi her zaman koşum zamanına ait bir gözlemdir, kaynağın var olup olmadığına
dair bir iddia değildir.

#### Özet

- Trait sınırı, bir genel fonksiyonun tip parametresine "bu trait'i gerçekleştirmiş olmalı"
  koşulunu koyar; kısa biçim (`<T: Etiket>`) ve `where` yan tümcesi derleyici için aynı
  anlama gelir.
- Birden fazla sınır `+` ile birleştirilir (`T: Etiket + Clone`); gövde tek kalır, sınır
  sayısı arttıkça çalışan kod artmaz.
- Sınırlı bir gövde (`sinirli-govde-sade`) ile aynı işi yapan sınırsız bir gövde
  (`somut-govde`) her seferinde aynı sayıda koştu: ikisi de iki kez — sınır koşum defterine
  hiçbir adım eklemiyor.
- Sınırın gerçek bedeli koşumda değil derlemede: her somut tip için ayrı bir gövde üretiliyor,
  ama bu üretim koşum sayacıyla ölçülemez ve ders bunu ölçmeye kalkmıyor.

#### Sonraki Adım

Sınır, bir trait'i tip parametresine dıştan bağladı. Bir trait bazen sözleşmenin **kendi
içinde** bir tip taşımak isteyebilir — çağıranın hiç belirtmediği, gerçekleştirenin
belirlediği bir tip. Sıradaki ders bu ilişkili tipi, aynı sözleşmeyi bir de tip parametresiyle
yazarak karşılaştırıyor.

### İlişkili Tipler

Source: https://academia.sh/tr/kurslar/rust-soyutlama/iliskili-tipler

Bir trait, tip parametresi almadan kendi içinde bir tip taşıyabilir — ilişkili tip; aynı sözleşme ilişkili tiple ve genellik parametresiyle yazıldığında koşum sayısı birebir aynı çıkar, fark çağıranın imzada ne yazmak zorunda kaldığındadır ve ilişkili tip bir tip için yalnız tek bir gerçekleştirime izin veren bir kısıt taşır.

Önceki ders bir trait sınırının bir tip parametresini **dıştan** nasıl kısıtladığını gösterdi:
`T: Etiket` yazımı, çağıranın hangi tipleri geçirebileceğini söylüyordu. Bir trait bazen
tersini de yapabilir — sözleşmenin **kendi içinde** bir tip taşıyabilir, bu tipi çağıran değil
gerçekleştiren belirler. Kursun ilk dersinde koşan çekirdekte buna zaten bir örnek vardı:
`Add` trait'inin gerçekleştirimi `type Output = Uzunluk;` satırını taşıyordu — işleç
bölümünün bu satırı, tam olarak bu dersin konusu olan **ilişkili tip**tir. O ders bunu
tanıtmadan geçti; bu ders ona geri dönüyor.

Bir trait sınırı çağırana "bu fonksiyona hangi tipleri geçirebilirsin" sorusunu yanıtlıyordu.
İlişkili tip farklı bir soruyu yanıtlıyor: "bu sözleşmeyi gerçekleştiren bir tip, kendisiyle
birlikte hangi **başka** tipi taşır." Bir trait yalnız yöntem imzaları değil, o yöntemlerin
işlediği yardımcı bir tip de bildirebiliyor — bu yardımcı tip her gerçekleştirimde farklı
olabilir, ama tek bir gerçekleştirim için sabittir. Bu, bir sözleşmenin yalnız "ne
yapılacağını" değil, "hangi tiple çalışılacağını" da tanımlayabildiği anlamına geliyor.

Bu derste ölçülen iki gövde de kursun üç sınıfından yalnız birine giriyor: **her seferinde**.
Ne ilişkili tip ne genellik parametresi, bir gövdenin ertelenmesine ya da atlanmasına yol
açıyor — ikisi de yalnız gövdenin **imzasını** biçimlendiriyor, gövdenin çağrıldığında koşup
koşmayacağını değil. Bu, önceki iki dersle aynı gözlem: sınıf, gövdenin nasıl adlandırıldığına
ya da nereye bağlandığına değil, çağrıldığında ne olduğuna bakılarak belirleniyor.

#### İlişkili Tip Nedir

Bir trait bildiriminin içinde `type Ad;` yazımı, "bu sözleşmeyi gerçekleştiren her tip, kendi
içinde bir `Ad` tipi de belirlemeli" demektir. Gerçekleştirme sırasında bu boşluk
`type Ad = SomutTip;` satırıyla doldurulur. Aşağıdaki iki trait aynı işi iki farklı yazımla
tanımlıyor:

```rust
// kere.rs — kesit: say/kere defteri, iliskili-tip ve genellik-parametresi sozlesmeleri.

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

pub type Defter = Rc<RefCell<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)
}

// Sozlesme kendi icinde bir tip tasiyor: Cikti, gerceklestiren tarafindan belirlenir.
pub trait Sozlesme {
    type Cikti;
    fn uret(&self, defter: &Defter) -> Self::Cikti;
}

// Ayni fikir, genellik parametresiyle: C caginin disaridan verdigi bir tip.
pub trait SozlesmeGenel<C> {
    fn uret_genel(&self, defter: &Defter) -> C;
}

pub struct Kutu {
    pub deger: i32,
}

impl Sozlesme for Kutu {
    type Cikti = i32;
    fn uret(&self, defter: &Defter) -> i32 {
        say(defter, "iliskili-tip-govdesi");
        self.deger
    }
}

impl SozlesmeGenel<i32> for Kutu {
    fn uret_genel(&self, defter: &Defter) -> i32 {
        say(defter, "genel-parametre-govdesi");
        self.deger
    }
}
```

`Self::Cikti` yazımındaki `Self::` öneki gelişigüzel değil: trait bildirimi yazılırken
henüz hangi tipin bu sözleşmeyi gerçekleştireceği bilinmiyor, bu yüzden `Cikti`ye çıplak bir
ad olarak değil, "gerçekleştiren tipin kendi `Cikti`si" anlamına gelen `Self::Cikti` olarak
yazılması gerekiyor. `impl` bloğunun içinde bu belirsizlik ortadan kalkıyor — `Self` artık
`Kutu`ya karşılık geldiği için `type Cikti = i32;` satırı doğrudan `i32` yazabiliyor, `Self::`
önekine gerek kalmıyor. Aynı isim (`Cikti`), trait bildiriminde bir **yer tutucu**,
gerçekleştirim içinde bir **karar** olarak iki farklı rolde görünüyor.

`Sozlesme`de `Cikti` sözleşmenin bir **parçası**: `uret` yönteminin dönüş tipi `Self::Cikti`
diye yazılıyor, somut bir tip adı değil. `Kutu` bu sözleşmeyi gerçekleştirirken `Cikti`nin
`i32` olduğunu bir kez, `impl` bloğunun başında bildiriyor; bloğun geri kalanı bu kararı
miras alıyor. `SozlesmeGenel<C>`de ise aynı bilgi trait'in **adının içinde**: `Kutu` için
`impl SozlesmeGenel<i32> for Kutu` yazıldığında `C` orada `i32`ye çözülüyor, ama bu bilgi
trait adının parçası olduğu için birden fazla kez, farklı `C` değerleriyle tekrarlanabilir —
aşağıda bu farkın kısıt tarafına bakılacak.

#### Aynı Sayı, Farklı Yazım

İki yazımı da aynı sayıda çağırıp koşum sayısını karşılaştıralım.

```rust
// iliskili-tip.rs — ayni is iki yazimla: iliskili tip ve genellik parametresi.
mod kere;
use kere::{kere, Defter, Kutu, Sozlesme, SozlesmeGenel};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let kutu = Kutu { deger: 9 };

    // Cagiran taraf, iliskili tipte Cikti'yi hic yazmiyor - derleyici kendisi biliyor.
    let _ = kutu.uret(&defter);
    let _ = kutu.uret(&defter);
    let _ = kutu.uret(&defter);

    // Genellik parametresinde de ayni sayida cagri, ayni govde.
    let _ = SozlesmeGenel::<i32>::uret_genel(&kutu, &defter);
    let _ = SozlesmeGenel::<i32>::uret_genel(&kutu, &defter);
    let _ = SozlesmeGenel::<i32>::uret_genel(&kutu, &defter);

    let mut govdeler: BTreeMap<&str, u32> = BTreeMap::new();
    govdeler.insert("iliskili-tip-govdesi", kere(&defter, "iliskili-tip-govdesi"));
    govdeler.insert("genel-parametre-govdesi", kere(&defter, "genel-parametre-govdesi"));

    println!("{:<24}{}", "govde", "kac kez");
    for (ad, n) in &govdeler {
        println!("{:<24}{}", ad, n);
    }
}
```

```
govde                   kac kez
genel-parametre-govdesi 3
iliskili-tip-govdesi    3
```

**TR5.** İkisi de üç kez koştu — **her seferinde** sınıfına giren iki satır, birebir aynı
sayıda. Fark koşum defterinde değil, çağrı satırının kendisinde görünüyor: `kutu.uret(&defter)`
yazımı `Cikti`nin ne olduğunu hiç söylemiyor, çünkü derleyici bunu `Kutu`nun `Sozlesme`
gerçekleştiriminden zaten biliyor. `SozlesmeGenel::<i32>::uret_genel(&kutu, &defter)` yazımı
ise `i32`yi açıkça taşıyor — `Kutu` üzerinde `SozlesmeGenel`in birden fazla gerçekleştirimi
olabileceği için (aşağıda gösteriliyor), hangisinin çağrıldığını belirtmek çağıranın işi
haline geliyor.

Arayüzler, Hatalar ve Genellikler kursu Go'nun genelliklerini **bağ** ekseninden yazmıştı;
burada ölçülen o kursun sorduğu soru değil, iki yazımın **gövde sayısı** — ve sayı birebir
eşit çıkıyor. Bu, ilişkili tip ile genellik parametresinin çalışma zamanında farklı bir
maliyet taşımadığını gösteriyor; aralarındaki fark tamamen kaynağın **okunuşunda** ve
çağıranın **yazmak zorunda kaldığı** bilgide.

#### İlişkili Tip Tip Başınadır

Kısıt, trait'in **tamamı** için değil, her **gerçekleştirim** için ayrı ayrı geçerli.
`Kutu` için `Cikti` bir kez `i32`ye sabitlendi, ama başka bir tip aynı `Sozlesme`yi kendi
`Cikti`siyle gerçekleştirebilir — kısıtlanan şey "bu trait her yerde tek bir `Cikti`
kullanmalı" değil, "bu tip, bu trait için tek bir `Cikti` seçmiş olmalı."

```rust
// iki-tip.rs — iliskili tip trait basina degil, gerceklestirim basina sabittir.
mod kere;
use kere::{kere, Defter, Kutu, Sozlesme, SozlesmeGenel};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

struct Etiketli {
    ad: String,
}

impl Sozlesme for Etiketli {
    type Cikti = String;
    fn uret(&self, defter: &Defter) -> String {
        kere::say(defter, "etiketli-govdesi");
        self.ad.clone()
    }
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let kutu = Kutu { deger: 5 };
    let etiketli = Etiketli { ad: String::from("baslik") };

    let sayisal: i32 = kutu.uret(&defter);
    let metinsel: String = etiketli.uret(&defter);
    let _ = SozlesmeGenel::<i32>::uret_genel(&kutu, &defter);

    println!("sayisal={} metinsel={}", sayisal, metinsel);
    println!("iliskili-tip-govdesi={}", kere(&defter, "iliskili-tip-govdesi"));
    println!("etiketli-govdesi={}", kere(&defter, "etiketli-govdesi"));
}
```

```
sayisal=5 metinsel=baslik
iliskili-tip-govdesi=1
etiketli-govdesi=1
```

**TR8.** `Kutu` `Cikti = i32` ile, `Etiketli` `Cikti = String` ile aynı `Sozlesme`yi
gerçekleştiriyor ve ikisi de aynı kaynakta yan yana duruyor — bu, trait'in kendisinin bir
tipe bağlı olmadığını, yalnız *o tipin gerçekleştirimine* bağlı olduğunu gösteriyor. Kısıt
hâlâ geçerli: ne `Kutu` ne `Etiketli` `Sozlesme`yi ikinci bir `Cikti` ile bir daha
gerçekleştiremez. Örnek ayrıca `Kutu` üzerinde `SozlesmeGenel<i32>`i de bir kez daha
çağırıyor — iki ayrı sözleşmenin (`Sozlesme` ve `SozlesmeGenel`) aynı kaynakta ve aynı tip
üzerinde bir arada durabildiğini, birbirini dışlamadığını göstermek için.

#### Genel Kod İçinde Kullanmak

Fark, tek bir somut tip (`Kutu`) üzerinde çağrı yaparken küçük görünüyor: ikisi de üç kelime
ekleyip ekleme farkı kadar kısa. Fark, `Sozlesme`yi ya da `SozlesmeGenel`i **başka bir genel
fonksiyonun sınırında** kullanmaya çalışınca büyüyor.

```rust
// genel-kullan.rs — Sozlesme'yi bir baska genel fonksiyonun sinirinda kullanmak.
mod kere;
use kere::{kere, say, Defter, Kutu, Sozlesme, SozlesmeGenel};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

// Cikti tipini ayrica yazmaya gerek yok - T::Cikti derleyici tarafindan cozuluyor.
fn kullan<T: Sozlesme>(x: &T, defter: &Defter) -> T::Cikti {
    say(defter, "genel-kullan-govdesi");
    x.uret(defter)
}

// Ayni fikir genellik parametresiyle: C'yi imzaya AYRICA eklemek gerekiyor.
fn kullan_genel<T: SozlesmeGenel<C>, C>(x: &T, defter: &Defter) -> C {
    say(defter, "genel-kullan-genel-govdesi");
    x.uret_genel(defter)
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let kutu = Kutu { deger: 9 };

    let a = kullan(&kutu, &defter);
    let b = kullan_genel(&kutu, &defter);

    println!("kullan={} kullan_genel={}", a, b);
    println!("genel-kullan-govdesi={}", kere(&defter, "genel-kullan-govdesi"));
    println!("genel-kullan-genel-govdesi={}", kere(&defter, "genel-kullan-genel-govdesi"));
}
```

```
kullan=9 kullan_genel=9
genel-kullan-govdesi=1
genel-kullan-genel-govdesi=1
```

**TR7.** `kullan<T: Sozlesme>`in dönüş tipi `T::Cikti` — `T` neye çözülürse çözülsün, onun
`Cikti`si otomatik olarak devralınıyor; imzada `Cikti` için ayrı bir yer yok, çünkü olması
gerekmiyor. `kullan_genel` ise sınırında `T: SozlesmeGenel<C>` yazdıktan sonra `C`yi
fonksiyonun **kendi** tip parametreleri listesine de eklemek zorunda kalıyor — `C` `T`den
otomatik olarak çıkarılamıyor, çünkü `T` teorik olarak `SozlesmeGenel`in birden fazla
gerçekleştirimini taşıyabilir ve derleyici hangisinin kastedildiğini bilemez. Bu, bir önceki
bölümdeki çağrı yerindeki farkın, genel kod yazarken bir **imza** farkına dönüştüğünü
gösteriyor: `kullan`in imzası tek tip parametresi taşırken `kullan_genel`inki iki taşıyor,
ikinci fonksiyonun tek işi ilkiyle aynı olsa bile.

Trait Sınırları dersindeki `birlestir<T: Etiket, U: Etiket>` örneği iki bağımsız tip
parametresinin nasıl bir arada durabildiğini göstermişti; `kullan_genel<T: SozlesmeGenel<C>, C>`
aynı yazımı kullanıyor, ama burada `C` bağımsız bir seçim değil, `T`nin zaten taşıdığı bir
bilginin tekrarı. İki fonksiyon da sözdizimsel olarak aynı kalıba uyuyor — `<...>` içinde
birden fazla parametre — ama `birlestir`de her parametre gerçekten farklı bir soruyu
yanıtlarken, `kullan_genel`de `C` yalnız `T`nin taşıdığı bir bilgiyi çağıranın yeniden
yazmasını istiyor. İlişkili tip bu tekrarı ortadan kaldırıyor.

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

İlişkili tip, sözleşmeye bir **kısıt** ekliyor: bir tip, `Sozlesme`yi yalnız **bir kez**
gerçekleştirebilir, çünkü `Cikti` o tip için tek bir değere bağlanıyor. `Kutu` için
`Cikti = i32` bir kez bildirildikten sonra, aynı tip için ikinci bir `impl Sozlesme for Kutu`
bloğu — `Cikti`yi `String` yapmaya çalışan bir ikincisi — derleyici tarafından reddedilir;
bu redd kod bloğuna konmuyor, çünkü derlenmeyen kaynak bu depoda hiçbir zaman kod bloğu
olarak gösterilmiyor. Kısıtın kendisi düzyazıyla tarif ediliyor; kabul edilen karşılığı
yukarıdaki `iliskili-tip.rs` zaten koşturdu — `Kutu` için tek bir `Sozlesme` gerçekleştirimi
var ve kaynak bu haliyle derleniyor.

`SozlesmeGenel<C>` aynı kısıtı taşımıyor: `C` trait adının bir parçası olduğu için,
`SozlesmeGenel<i32>` ile `SozlesmeGenel<String>` derleyici için **iki ayrı trait**tir ve
`Kutu` ikisini de aynı anda gerçekleştirebilir.

```rust
// kisit.rs — genellik parametresi ayni tip icin birden fazla gerceklestirime izin verir;
// iliskili tip vermez (bir tip = bir Cikti baglamasi).
trait SozlesmeGenel<C> {
    fn uret_genel(&self) -> C;
}

struct Kutu {
    deger: i32,
}

impl SozlesmeGenel<i32> for Kutu {
    fn uret_genel(&self) -> i32 {
        self.deger
    }
}

impl SozlesmeGenel<String> for Kutu {
    fn uret_genel(&self) -> String {
        self.deger.to_string()
    }
}

fn main() {
    let kutu = Kutu { deger: 9 };
    let sayisal: i32 = SozlesmeGenel::<i32>::uret_genel(&kutu);
    let metinsel: String = SozlesmeGenel::<String>::uret_genel(&kutu);
    println!("sayisal={} metinsel={}", sayisal, metinsel);
}
```

```
sayisal=9 metinsel=9
```

**TR6.** `Kutu`, `SozlesmeGenel<i32>` ve `SozlesmeGenel<String>`i aynı anda gerçekleştiriyor
— ikisi de kabul edildi, ikisi de derlendi. Bu, ilişkili tipin sunduğu kısıtın bir eksiklik
değil bilinçli bir tasarım tercihi olduğunu gösteriyor: `Sozlesme` kullanılsaydı `Kutu`nun
`Cikti`si tek bir değere sabitlenir, çağıran taraf hiçbir zaman "hangi `Cikti`?" diye
sormak zorunda kalmazdı. `SozlesmeGenel` kullanıldığında bu esneklik kazanılıyor, ama
bedeli çağrı yerinde görünüyor: `kutu.uret_genel(&defter)` yazımı `Kutu` iki
gerçekleştirimi barındırdığı için tek başına yeterli değil, hangi `C` kastedildiğinin
(`::<i32>` ya da `::<String>`) açıkça yazılması gerekiyor. İlişkili tipte bu belirtme hiç
gerekmiyor, çünkü zaten yalnız bir seçenek var.

Bu kısıt bir eksiklik olarak değil, bir **tasarım aracı** olarak okunmalı. Bir sözleşme, bir
tipin doğası gereği tek bir çıktı tipiyle çalıştığını öngörüyorsa (örneğin bir kap için
"içindeki eleman tipi" ya da bir dönüştürücü için "ürettiği sonuç tipi") ilişkili tip bu
tekliği bir sözdizimi kuralına dönüştürür: yazan taraf yanlışlıkla aynı tipi iki farklı
çıktı tipiyle gerçekleştiremez, çünkü derleyici buna izin vermiyor. Genellik parametresi ise
bu tekliği zorunlu kılmaz — bir tipin gerçekten birden fazla çıktı biçimiyle çalışabileceği
durumlarda (bu derste görüldüğü gibi, bir `Kutu`nun hem sayısal hem metinsel bir karşılığı
olabilir) doğru araç odur. İki yazımın da var olmasının nedeni, "bir tip = bir çıktı" ile
"bir tip = birden fazla çıktı" sorularının farklı sözleşmeler gerektirmesi.

#### Özet

- İlişkili tip, bir trait'in kendi içinde bir `type Ad;` bildirimiyle taşıdığı, gerçekleştiren
  tarafından doldurulan bir tip yuvasıdır; genellik parametresi aynı bilgiyi trait adının
  içinde taşır.
- İki yazım da her seferinde sınıfına giriyor ve koşum sayısı birebir aynı çıkıyor (üç ve
  üç); fark çalışma zamanında değil, çağıranın imzada yazmak zorunda kaldığı bilgide.
- İlişkili tip bir kısıt taşır: bir tip, bir trait'i ilişkili tipiyle yalnız bir kez
  gerçekleştirebilir.
- Genellik parametresi bu kısıtı taşımaz: aynı tip, aynı trait'i farklı parametre
  değerleriyle birden fazla kez gerçekleştirebilir — bedeli, çağıranın hangi gerçekleştirimi
  kastettiğini açıkça belirtmek zorunda kalmasıdır.

#### Sonraki Adım

Bu iki ders, aynı gövdenin sınır ya da ilişkili tiple yazıldığında koşum sayısının
değişmediğini gösterdi — ikisi de derleme zamanında çözülüyor. Sıradaki ders çağrının **ne
zaman** çözüldüğü sorusuna dönüyor: aynı sözleşme, derleme zamanında çözülen bir çağrıyla mı,
koşum sırasında çözülen bir çağrıyla mı kullanılıyor ve bu ikisi koşum sayısını değiştiriyor
mu?

### Nesne Güvenliği ve Dinamik Gönderim

Source: https://academia.sh/tr/kurslar/rust-soyutlama/nesne-guvenligi-ve-dinamik-gonderim

Bir trait sınırıyla yazılmış çağrı derleme zamanında, bir trait nesnesi (dyn) üzerinden yazılmış çağrı koşum sırasında çözülür; ölçüm çekirdeğinde ikisi de iki kez koşar — fark sayıda değil, çağrının nasıl bulunduğundadır ve bu, yalnız nesne güvenliği koşulunu sağlayan traitler için mümkündür.

İlişkili Tipler dersi, bir sözleşmeyi kullanmanın iki yolunun da (ilişkili tip, genellik
parametresi) **derleme zamanında** çözüldüğünü gösterdi. Bir trait'i kullanmanın üçüncü bir
yolu var ve bu yol derleme zamanında değil **koşum sırasında** çözülüyor: trait nesnesi.
Kursun ilk dersinde koşan çekirdekte bu ayrım zaten vardı — `statik<T: Etiket>` ile
`dinamik(x: &dyn Etiket)` aynı işi iki farklı çağrı biçimiyle yapıyordu. Bu ders o farkı
açıyor: çağrı ne zaman çözülürse çözülsün, gövde kaç kez koşuyor?

#### İki Çağrı Biçimi

`statik<T: Etiket>(x: &T)` yazımı önceki iki dersten tanıdık: `T` bir tip parametresi, sınırı
`Etiket`. Derleyici bu fonksiyonu her çağrıldığı somut tip için ayrı ayrı üretiyor — Trait
Sınırları dersinin gösterdiği gibi. `dinamik(x: &dyn Etiket)` yazımı farklı: `x`in tipi artık
bir tip parametresi değil, `dyn Etiket` — "`Etiket`i gerçekleştiren, tipi koşum sırasında
belirlenecek herhangi bir değere referans." `dyn` anahtar sözcüğü, derleyiciye "bu referansın
arkasındaki somut tipi şimdiden bilmiyorum, ama `Etiket`in yöntemlerini çağırabileceğimi
biliyorum" demenin yolu.

Bu iki biçimin var olma nedeni farklı sorulara yanıt vermeleri. Statik gönderim, çağrılan her
somut tip için ayrı bir gövde üretiyor — Trait Sınırları dersinin gösterdiği gibi, bu üretim
derleme zamanında olur ve koşum defterine hiç girmez. Ama bu üretim bir bedel de taşır: bir
fonksiyon `<T: Etiket>` yazdığında, o fonksiyonu çağıran her yer, hangi somut `T`nin
kullanıldığını derleme zamanında bilmek zorundadır. Bir liste, bir alan ya da bir dönüş değeri
"ya `Sade` ya `Ozel` olabilir" demek istiyorsa, statik gönderim bunu ifade edemez — tip
parametresi tek bir somut tipe çözülmek zorundadır. Dinamik gönderim tam bu boşluğu
dolduruyor: `dyn Etiket`, "hangi somut tip olduğu önemli değil, yeter ki `Etiket`i
gerçekleştirsin" diyerek farklı tipleri aynı yerde tutmayı mümkün kılıyor. Bu ders ikisinin
birbirinin **yerine** geçtiğini değil, birbirini **tamamladığını** gösteriyor.

Bu iki yazımın karşılığı ikili düzeyde de farklı. `statik` çağrıldığında derleyici hangi
yöntemin çalışacağını kaynaktan doğrudan okuyup çağrıya gömüyor. `dinamik` çağrıldığında ise
`x`in beraberinde bir **yöntem tablosu** taşıması gerekiyor — trait nesnesi aslında iki
parçadan oluşuyor: değerin kendisine bir referans ve o değerin somut tipine ait yöntemlerin
adreslerini tutan bir tablo. Çağrı sırasında hangi gövdenin çalışacağı bu tablodan
**bulunuyor**; kaynakta yazan tek satır (`x.ad()`), `x`in arkasındaki tipe göre iki farklı
gövdeye açılabiliyor.

`Box<dyn Etiket>` iki kelimeden kuruluyor ve ikisi de ayrı bir kursta zaten tanıtıldı. `Box`,
öbekte tekil sahiplik kuran bir kap: içindeki değeri öbeğe yerleştirir ve tek bir sahip
tutar. `dyn Etiket` ise bu kabın içinde **hangi** somut tipin durduğunu adlandırmıyor, yalnız
hangi sözleşmeyi karşıladığını söylüyor. İkisi birleştiğinde ortaya çıkan `Box<dyn Etiket>`,
"öbekte duran, `Etiket`i gerçekleştiren, ama tam olarak hangi tip olduğu unutulmuş bir değer"
anlamına geliyor. Bu unutma bilinçli: `Box<Sade>` ile `Box<Ozel>` derleyici için iki farklı
tiptir ve aynı diziye konamaz; `Box<dyn Etiket>` ikisinin de ortak paydası olduğu için aynı
diziye konabiliyor.

#### Koşum Sayısı Değişmiyor

```rust
// kere.rs — kesit: statik ve dinamik gonderim icin Etiket, Sade, Ozel.

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

pub type Defter = Rc<RefCell<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)
}

pub trait Etiket {
    fn defteri(&self) -> &Defter;
    fn ad(&self) -> String {
        String::from("varsayilan")
    }
}

pub struct Sade {
    pub defter: Defter,
}

impl Etiket for Sade {
    fn defteri(&self) -> &Defter {
        &self.defter
    }
}

pub struct Ozel {
    pub defter: Defter,
}

impl Etiket for Ozel {
    fn defteri(&self) -> &Defter {
        &self.defter
    }
    fn ad(&self) -> String {
        String::from("ozel")
    }
}

pub fn statik<T: Etiket>(x: &T) -> String {
    say(x.defteri(), "statik-gonderim");
    x.ad()
}

pub fn dinamik(x: &dyn Etiket) -> String {
    say(x.defteri(), "dinamik-gonderim");
    x.ad()
}
```

```rust
// gonderim.rs — statik ve dinamik gonderimin kosum sayisini karsilastirir.
mod kere;
use kere::{kere, say, Defter, Etiket, Ozel, Sade, dinamik, statik};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let sade = Sade { defter: Rc::clone(&defter) };
    let ozel = Ozel { defter: Rc::clone(&defter) };

    let _ = statik(&sade);
    let _ = statik(&sade);

    let _ = dinamik(&sade as &dyn Etiket);
    let _ = dinamik(&ozel as &dyn Etiket);

    // Ayni trait nesnesi, farkli somut tipleri tek bir listede tutabiliyor - statik
    // gonderim bunu yapamazdi, cunku bir Vec<T> tek bir somut T'ye kilitlenir.
    let liste: Vec<Box<dyn Etiket>> = vec![
        Box::new(Sade { defter: Rc::clone(&defter) }),
        Box::new(Ozel { defter: Rc::clone(&defter) }),
        Box::new(Ozel { defter: Rc::clone(&defter) }),
    ];
    // Dongunun kendisi kac eleman oldugunu biliyor, ama her cagrida hangi govdenin
    // kosacagini bilmiyor - bu, elemanin gercek tipine gore ad() icinde cozuluyor.
    let adlar: Vec<String> = liste
        .iter()
        .map(|e| {
            say(e.defteri(), "liste-govdesi");
            e.ad()
        })
        .collect();

    let mut govdeler: BTreeMap<&str, u32> = BTreeMap::new();
    govdeler.insert("statik-gonderim", kere(&defter, "statik-gonderim"));
    govdeler.insert("dinamik-gonderim", kere(&defter, "dinamik-gonderim"));
    govdeler.insert("liste-govdesi", kere(&defter, "liste-govdesi"));

    println!("{:<18}{}", "govde", "kac kez");
    for (ad, n) in &govdeler {
        println!("{:<18}{}", ad, n);
    }
    println!("liste-adlari={:?}", adlar);
}
```

```
govde             kac kez
dinamik-gonderim  2
liste-govdesi     3
statik-gonderim   2
liste-adlari=["varsayilan", "ozel", "ozel"]
```

**TR9.** `statik-gonderim` iki kez koştu, `dinamik-gonderim` de iki kez — birebir aynı sayı.
Fark sayıda değil: iki çağrı biçimi de gövdeyi kaçınılmaz biçimde tetikliyor, ikisi de **her
seferinde** sınıfına giriyor. Nesneye Dayalı Java kursu *kararı hangi tipin verdiğini*
ölçmüştü — bildirilen tip mi çalışma zamanı tipi mi. Burada ölçülen o soru değil; ölçülen, iki
gönderim biçiminin **gövde sayısını değiştirip değiştirmediği** — değiştirmiyor. `Sade` ve
`Ozel`in `ad` yöntemleri kaç kez çağrılırsa çağrılsın, çağrı biçiminin statik ya da dinamik
olması bu sayıyı bir artırmıyor ya da eksiltmiyor.

`liste-govdesi` satırı bunun pratik sonucunu gösteriyor: `Vec<Box<dyn Etiket>>`, farklı somut
tipleri (`Sade` ve iki ayrı `Ozel`) **tek bir koleksiyonda** tutabiliyor — Sahiplik ve Bellek
Modeli kursunun Öbekte Tekil Sahiplik dersinde kurulan `Box`, burada içine bir trait nesnesi
alıyor. `statik<T: Etiket>` bunu yapamazdı: bir `Vec<T>` tek bir somut `T` tipine kilitlidir,
aynı dizi içinde `Sade` ile `Ozel`i bir arada tutamaz. Üç elemanlı listenin gövdesi üç kez
koştu — bu da **her seferinde** sınıfına giriyor, çünkü dönüşüm dizinin her elemanı için
kaçınılmaz. Dikkat edilmesi gereken şey, `map` içindeki kapanışın kendisinin `e`nin gerçek
tipini hiç bilmemesi: `e.ad()` çağrısı, listenin üçüncü elemanına ulaşana kadar hangi gövdenin
çalışacağını söylemiyor, bunu yalnız o elemanın taşıdığı yöntem tablosu biliyor. Bu, iki
çağrı biçiminin koşum sayısı eşit olsa da aynı işi görmediğini gösteriyor — trait nesnesi,
çeşitliliği bir koleksiyonun içine taşıyabiliyor, statik gönderim taşıyamıyor.

#### Nesne Güvenliği

`dyn Etiket` yazımının derlenebilmesi, `Etiket`in **nesne güvenli** olmasına bağlı. Bir trait
nesne güvenli değilse, o trait için bir yöntem tablosu kurulamıyor ve `dyn` yazımı derleyici
tarafından reddediliyor. İki yaygın engel var: bir yöntem `Self`i **değer olarak** döndürüyorsa
(`fn kopya(&self) -> Self` gibi), tablo bu yöntemin dönüş boyutunu önceden bilemiyor — çünkü
`dyn Etiket`in arkasındaki somut tip koşum sırasında değişebiliyor ve her tipin boyutu farklı
olabiliyor. Bir yöntem kendi **tip parametresi** taşıyorsa (`fn cevir<U>(&self, u: U)` gibi),
tablo sonsuz sayıda `U` için ayrı ayrı giriş tutamaz. Bu iki yazım, düzyazıda tarif edildiği
haliyle burada reddediliyor; derlenmedikleri için kod bloğuna konmuyorlar.

Her iki engelin ortak kökeni aynı: yöntem tablosunun her girdisi, çağrı sırasında sabit ve
önceden bilinen boyutta bir işaretçi kümesi olmalı. `Self`i değer olarak döndüren bir yöntem
tabloya konursa, çağıran taraf "bu çağrının döndürdüğü değer ne kadar yer kaplayacak"
sorusuna derleme zamanında yanıt bulamaz — çünkü `dyn Etiket`in arkasında `Sade` de olabilir,
başka bir boyutta bir tip de. Kendi tip parametresi taşıyan bir yöntem ise tabloya tek bir
giriş olarak sığmaz, çünkü her farklı `U` için ayrı bir gövde gerekir ve tablo sonsuz sayıda
`U`yu önceden listeleyemez. İki engel de aynı ilkeye çıkıyor: trait nesnesi, gerçekleştiren
tipin kimliğini **unutmuş** bir referanstır; bir yöntem bu unutulan kimliğe (`Self`in tam
boyutuna ya da çağıranın seçtiği bir `U`ya) dayanıyorsa, tablo o yöntemi barındıramaz.

`Etiket` bu iki engeli de taşımıyor: `defteri` ve `ad` ikisi de `&self` alıyor (değer olarak
`Self` değil), ikisi de kendi tip parametresi taşımıyor. Bu yüzden nesne güvenli ve
yukarıdaki `dinamik` fonksiyonu, `&dyn Etiket` parametresiyle, sorunsuz derleniyor —
kabul edilen tasarımın karşılığı zaten koşturuldu. Bir trait yazarken hangi yöntemlerin
trait nesnesi olarak kullanılabilmesini istediğine karar vermek, o yöntemlerin imzasını
baştan bu iki kurala göre şekillendirmek anlamına geliyor.

Bu koşul, önceki dersteki ilişkili tip kısıtına benzer bir yerde duruyor: ikisi de bir
sözleşmenin **her** kullanım biçimiyle uyumlu olmayabileceğini gösteriyor. İlişkili tip
kısıtı bir tipin bir sözleşmeyi kaç kez gerçekleştirebileceğini sınırlıyordu; nesne güvenliği
ise bir sözleşmenin **hangi çağrı biçimleriyle** kullanılabileceğini sınırlıyor. `Etiket`in
kendisi bu ikisinden de etkilenmiyor çünkü baştan basit tutuldu: hem tek bir ilişkili tipe
ihtiyaç duymuyor hem de nesne güvenli yöntemlerle sınırlı kalıyor. Daha geniş bir sözleşme
yazan bir trait bu iki kısıttan birine ya da ikisine birden çarpabilir; hangisine çarptığı
trait'in imzasından, koşturmadan önce okunabilir.

Örneğin, iki değeri karşılaştırıp büyük olanı **değer olarak** döndüren bir yöntem —
`fn buyuk_olan(&self, oteki: Self) -> Self` — nesne güvenli olmazdı: `Self` burada hem
parametre hem dönüş tipi olarak geçiyor ve `dyn Etiket`in arkasındaki gerçek tip koşum
sırasında değişebildiği için, tablo bu yöntemin girdi ve çıktı boyutunu önceden sabitleyemez.
Bu yöntem `Etiket`e hiç eklenmedi; eklenseydi `&dyn Etiket` yazımı derlenmeyecekti. Aynı
karşılaştırma isteği, değeri değil bir **referans** ya da bir **sıralama sonucu** (küçük,
eşit, büyük gibi sabit boyutlu bir bilgi) döndürerek nesne güvenli kalabilirdi — bu da bu
kursun genel gözlemiyle örtüşüyor: bir sözleşmenin sınırları, o sözleşmeyi çağıran taraf
değil, gerçekleştirenin unutulmuş kimliğiyle çalışmak zorunda kalan **tablo** çiziyor.

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

Aynı sayı, aynı bedel demek değil. `statik-gonderim` ile `dinamik-gonderim`in ikisi de iki
kez koşması, iki çağrı biçiminin **çalışma zamanında birbirinin aynı** olduğu anlamına
gelmiyor — sayı yalnız gövdenin **kaç kez tetiklendiğini** ölçüyor, çağrının **nasıl
bulunduğunu** değil. `statik` çağrısında hangi gövdenin çalışacağı kaynaktan okunarak
derleme sırasında belirleniyor; `dinamik` çağrısında aynı bilgi bir yöntem tablosundan
koşum sırasında aranıyor. Bu aramanın kendisi bir adım mı, adımsız mı — bu soru koşum
sayacıyla ölçülemez, çünkü sayaç yalnız `say` çağrısının kaç kez geçtiğini sayıyor, o
çağrıya ulaşmak için kaç basamak izlendiğini değil. Bu soru zaten Nesneye Dayalı Java
kursunda ele alınmıştı; bu ders onu tekrar sormuyor, yalnız iki biçimin **gövde sayısını**
eşit bıraktığını gösteriyor.

Bu ayrımın somut bir sonucu var: koşum defterine bakarak "bu kaynak dinamik gönderim mi
kullanıyor, statik mi" sorusuna yanıt verilemez. `statik-gonderim` ile `dinamik-gonderim`
satırları yan yana durduğunda, ikisinin de iki olması bir okuyucuyu yanlış bir sonuca
götürebilir — "ikisi aynı şeyi yapıyorsa hangisini seçtiğim önemsiz" sonucuna. Oysa önemli:
`liste-govdesi` satırının var olabilmesi, yalnız `dyn Etiket` biçiminin nesne güvenliği
koşulunu sağlamasına bağlıydı; aynı liste `statik`in kullandığı sınırla hiç kurulamazdı.
Koşum sayısı iki biçimi ayırt etmiyor olsa da, hangi **yazımların** mümkün olduğu ayrı bir
eksende hâlâ ayrışıyor — bu ders o eksenin sayıyla değil, kodun kendisiyle görüldüğünü
gösteriyor.

Bu kursun ilk dört dersi, bir gövdenin çevresine konan her şeyin — varsayılan gövde, sınır,
ilişkili tip, çağrı biçimi — koşum sayısına dokunup dokunmadığını sırayla sordu. Dördünde de
yanıt aynı çıktı: dokunmuyor. Gövde nasıl bulunursa bulunsun (bir tip parametresinden ya da
bir yöntem tablosundan), çağrıldığı her yerde kaçınılmaz biçimde koşuyor ve bu, üzerine
hangi sözdizimi kararının bindirildiğinden bağımsız.

#### Özet

- Statik gönderim (`<T: Etiket>`) derleme zamanında, dinamik gönderim (`&dyn Etiket`) koşum
  sırasında bir yöntem tablosundan çözülür.
- İki biçim de her seferinde sınıfına giriyor ve koşum sayısı birebir aynı çıkıyor: iki ve
  iki — çağrı biçimi gövdenin kaç kez tetiklendiğini değiştirmiyor.
- Trait nesnesi (`Box<dyn Etiket>`), farklı somut tipleri tek bir koleksiyonda tutabilir;
  statik gönderim bunu yapamaz çünkü bir tip parametresi tek bir somut tipe kilitlenir.
- Nesne güvenliği, bir trait'in `dyn` ile kullanılabilmesi için gereken koşuldur: `Self`i
  değer olarak döndüren ya da kendi tip parametresi taşıyan yöntemler bu koşulu bozar.
- Aynı koşum sayısı aynı bedel anlamına gelmez; çağrının nasıl bulunduğu (derleme zamanı mı,
  koşum zamanı mı) koşum sayacıyla ölçülemeyen ayrı bir sorudur.

#### Sonraki Adım

Bu derste gövdenin kendisi hep elle yazılıydı — `ad` yönteminin gövdesi ya trait'in
varsayılanından ya da `Ozel`in kendi yazdığından geliyordu. Bir gövde hiç elle yazılmadan da
üretilebilir. Sıradaki ders, kaynakta hiçbir satırı görünmeyen bir gövdenin koşum sırasında
nasıl çalıştığını ve bu çalışmanın neden koşum sayacıyla sayılamadığını gösteriyor.

### Türetilen Trait'ler

Source: https://academia.sh/tr/kurslar/rust-soyutlama/turetilen-traitler

Bir öznitelik satırı, bir trait'i elle yazmadan gerçekleştirebilir — türetme; elle yazılan gövde koşum defterine bir sayaç çağrısı koyabilirken, türetilmiş gövde kaynakta hiç yazılı olmadığı için sayılamaz ve yalnız davranışıyla gözlemlenebilir; üstelik aynı türetme iki farklı veri düzeninde iki farklı sonuç verir.

Önceki dört ders elle yazılan gövdeleri ölçtü: bir trait'in varsayılan gövdesi, sınırlı bir
genel gövde, ilişkili tip taşıyan bir gövde, statik ya da dinamik çağrılan bir gövde — hepsi
kaynakta bir yerde, bir `fn` satırının altında duruyordu. Bir trait gerçekleştirimi bazen hiç
elle yazılmaz: `#[derive(...)]` öznitelik satırı, derleyiciye "bu trait'i bu tip için sen
üret" der ve gövdeyi kaynağa hiç yazmadan ortaya çıkarır. Bu ders bu türden bir gövdenin
kaç kez koştuğunu soruyor — ve bu kez soruya tam bir sayıyla yanıt verilemediğini gösteriyor.

#### Elle Yazılan ile Türetilmiş Arasındaki Fark

Bir tip için eşitlik karşılaştırması iki yolla kurulabilir: `impl PartialEq for Tip` bloğunu
elle yazarak, ya da `#[derive(PartialEq)]` satırını tipin üstüne koyarak. İkisi de aynı
sonucu üretir — iki değerin alan alan eşit olup olmadığını söyler — ama kaynaktaki
karşılıkları farklı.

```rust
// kere.rs — kesit: say/kere defteri, elle yazilan esitlik govdesi.

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

pub type Defter = Rc<RefCell<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)
}

// Elle yazilan gerceklestirim: govde kaynakta duruyor, sayilabiliyor.
pub struct ElleEsit {
    pub x: i32,
    pub y: i32,
    pub defter: Defter,
}

impl PartialEq for ElleEsit {
    fn eq(&self, oteki: &Self) -> bool {
        say(&self.defter, "elle-esitlik-govdesi");
        self.x == oteki.x && self.y == oteki.y
    }
}

// Turetilmis gerceklestirim: govde kaynakta yok, derleyici alan alan karsilastirma uretiyor.
#[derive(PartialEq)]
pub struct TuretilmisEsit {
    pub x: i32,
    pub y: i32,
}
```

`ElleEsit`in `eq` yöntemi, kaynakta iki satırlık bir gövde taşıyor: önce bir sayma çağrısı,
sonra alan alan karşılaştırma. `TuretilmisEsit` ise hiçbir `impl PartialEq` bloğu taşımıyor —
tek satırı `#[derive(PartialEq)]` ve bu satır bir yöntem gövdesi değil, derleyiciye bir
**talimat**. Derleyici bu talimatı okuyunca kendi `eq` gövdesini üretiyor; bu gövde
`ElleEsit`inkiyle aynı işi yapıyor (`x` alanlarını ve `y` alanlarını karşılaştırmak) ama
kaynakta hiçbir satırda görünmüyor.

Tek bir `#[derive(...)]` satırı birden fazla trait'i aynı anda üretebilir; parantez içine
virgülle ayrılmış trait adları yazılır. Bu kursta zaten görülen `Clone` da bu yolla
türetilebilir, `Debug` (bir değeri okunabilir bir metin kalıbına döken trait) da:

```rust
// turetme-debug.rs — Debug turetmesi, alanlari otomatik bir metin kalibina donusturur.
#[derive(Debug, Clone, PartialEq)]
struct Nokta {
    x: i32,
    y: i32,
}

fn main() {
    let a = Nokta { x: 3, y: 5 };
    let b = a.clone();
    println!("{:?}", a);
    println!("esit_mi={}", a == b);
}
```

```
Nokta { x: 3, y: 5 }
esit_mi=true
```

Üç trait de (`Debug`, `Clone`, `PartialEq`) tek satırda türetildi ve üçünün de gövdesi
kaynakta görünmüyor. `{:?}` çıktısı, `Debug`in ürettiği gövdenin alan adlarını ve
değerlerini kendiliğinden bir araya getirdiğini gösteriyor — bu kalıp da elle yazılmadı, tıpkı
`eq` gibi derleyici tarafından üretildi. Bu üç trait birlikte türetilebiliyor çünkü üçü de
aynı ilkeye dayanıyor: yapının her alanı için aynı işlemi (yazdırmak, kopyalamak,
karşılaştırmak) sırayla uygulamak. Bu ortak ilke, ilerleyen bir derste (dizileştirme)
tekrar karşımıza çıkacak.

#### Sayılabilen ve Sayılamayan

```rust
// turetme-sayilamaz.rs — elle yazilan govde sayilir, turetilmis govde sayilamaz.
mod kere;
use kere::{kere, Defter, ElleEsit, TuretilmisEsit};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let a = ElleEsit { x: 1, y: 2, defter: Rc::clone(&defter) };
    let b = ElleEsit { x: 1, y: 2, defter: Rc::clone(&defter) };
    let c = ElleEsit { x: 9, y: 9, defter: Rc::clone(&defter) };

    let esit_mi = a == b;
    let esit_degil_mi = a == c;
    println!("elle: esit_mi={} esit_degil_mi={}", esit_mi, esit_degil_mi);
    println!("elle-esitlik-govdesi={}", kere(&defter, "elle-esitlik-govdesi"));

    // Turetilmis PartialEq'in govdesi kaynakta yazili degil - bu yuzden bir sayma
    // cagrisi icine koyamayiz. Yalnizca davranisini gozlemleyebiliriz.
    let p = TuretilmisEsit { x: 1, y: 2 };
    let q = TuretilmisEsit { x: 1, y: 2 };
    let r = TuretilmisEsit { x: 9, y: 9 };
    println!("turetilmis: esit_mi={} esit_degil_mi={}", p == q, p == r);
}
```

```
elle: esit_mi=true esit_degil_mi=false
elle-esitlik-govdesi=2
turetilmis: esit_mi=true esit_degil_mi=false
```

**TR10.** `elle-esitlik-govdesi` iki kez koştu — `a == b` ve `a == c` çağrılarının her ikisi
de `eq` gövdesini tetikledi, çünkü gövdenin içine bir sayma çağrısı elle yerleştirildi. Bu
satır **her seferinde** sınıfına giriyor: `==` işleci her kullanıldığında kaçınılmaz biçimde
çalışıyor. `TuretilmisEsit` için aynı satırı yazamıyoruz — derleyicinin ürettiği gövdenin
içine hiçbir kaynak satırı ekleyemediğimiz için, kaç kez çalıştığını gösteren bir sayaç
koyamıyoruz. `p == q` ve `p == r` çağrılarının **ikisinin de** doğru sonucu verdiğini
görebiliyoruz (davranış gözlemi), ama bu iki çağrının arkasındaki gövdenin kaç kez
tetiklendiğini koşum defterine yazdıramıyoruz. Bu, kaynakta yazılı olmayan bir gövdenin
kursun üç sınıfından **hiçbirine kesin biçimde yerleştirilemediğini** gösteriyor — çalıştığı
kesin (çağrı her yapıldığında bir sonuç dönüyor), ama koşum sayısı bu yöntemle ölçülemez.

Nesneye Dayalı Java kursu, kayıt (record) tipinin eşitlik ve metin gösterimini otomatik
kurduğunu göstermişti — orada ölçülen kararı hangi tipin verdiğiydi, kaydın kendi ürettiği
gövdenin çalışıp çalışmadığı değil. Bu ders benzer bir otomatik üretime bakıyor, ama farklı
bir soru soruyor: üretilen bu gövde, kursun saymayı esas alan yöntemine göre sayılabilir mi?
Yanıt hayır — ders bunu açıkça söylüyor, bir sayı uydurmuyor.

Türetilmiş bir gövdenin sayılamaması, onun rastgele ya da öngörülemez olduğu anlamına
gelmiyor — tam tersine, `#[derive(PartialEq)]`in ne üreteceği tamamen belirlenimci: yapının
alanlarını bildirim sırasıyla tek tek karşılaştırır, hepsi eşitse `true`, biri bile
eşitsizse `false` döner. Bu kural her `#[derive(PartialEq)]` için aynıdır ve yapı
bildirimine bakarak koşturmadan bile önceden söylenebilir. Sayılamayan şey davranışın
kendisi değil, o davranışın çalışma zamanındaki **çağrı sayısı**: kaç kez `eq` çalıştığını
görmek istiyorsak koşum defterine bir satır eklememiz gerekiyor ve türetilmiş kaynakta
eklenecek bir satır yok.

"Gövde kaynakta yok" demek, "hiçbir gövde derlenmiyor" demek değil. `#[derive(...)]`
satırı, derleme sırasında `impl` bloğuna benzer bir kod parçası **üretiyor** ve bu üretilmiş
kod, elle yazılmış bir `impl` bloğuyla aynı yolla derleniyor — üretim, derlemenin geri
kalanından önce, ayrı bir aşamada oluyor. Yazan kişinin gördüğü kaynakta bu üretilmiş kod
hiç görünmüyor; yalnız `#[derive(PartialEq)]` satırı görünüyor, ürettiği onlarca satırlık
karşılaştırma mantığı görünmüyor. Bu ders "yazılmayan bir gövde de koşar" derken tam bunu
kastediyor: koşan şey var, gerçekten çalışıyor, ama yazan kişinin **okuduğu** kaynakta bir
karşılığı yok. Sayaç çağrısı ekleyemememizin nedeni de bu — sayaç eklemek için önce
üzerine ekleme yapılacak bir kaynak satırına ihtiyaç var ve türetilmiş gövdede öyle bir
satır elimizde değil.

#### Aynı Türetme, Farklı Veri Düzeni, Farklı Sonuç

Türetmenin kaynakta gövdesi olmaması, her tipte aynı anlama geldiği izlenimini
uyandırabilir. Değil: aynı `#[derive(Clone)]` satırı, iki farklı veri düzeninde iki farklı
davranış üretiyor. `derive` her zaman "alan alan aynı işlemi uygula" der, ama "aynı işlem"in
ne anlama geldiği alanın kendi tipine bağlı — bir `i32` için kopyalamak yeni bir sayı
yazmaktır, bir `Rc<...>` için kopyalamak sayaç artırıp aynı hücreyi paylaşmaktır.

```rust
// turetme-duzen.rs — ayni turetme, iki veri duzeninde iki farkli sonuc verir.
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Clone)]
struct Bagli {
    veri: Rc<RefCell<i32>>,
}

#[derive(Clone)]
struct Ayrik {
    veri: i32,
}

fn main() {
    let a = Bagli { veri: Rc::new(RefCell::new(1)) };
    let b = a.clone();
    *a.veri.borrow_mut() += 41;
    println!("bagli klon paylasiyor mu: {}", *b.veri.borrow() == 42);

    let c = Ayrik { veri: 1 };
    let mut d = c.clone();
    d.veri += 41;
    println!("ayrik klon paylasiyor mu: {}", c.veri == d.veri);
}
```

```
bagli klon paylasiyor mu: true
ayrik klon paylasiyor mu: false
```

**TR11.** `Bagli`nin klonu, kaynaktaki değeri **paylaşıyor**: `a` üzerinden yapılan
değişiklik `b`de de görünüyor, çünkü `Rc<RefCell<i32>>` alanının türetilmiş klonu yalnız
sayacı bir artırıp aynı hücreye ikinci bir referans veriyor — Sahiplik ve Bellek Modeli
kursunun Referans Sayımı dersinde kurulan davranış. `Ayrik`in klonu ise **bağımsız**: `d`
üzerinde yapılan değişiklik `c`yi hiç etkilemiyor, çünkü `i32` alanının türetilmiş klonu
kendi başına bir kopya üretiyor. `#[derive(Clone)]` satırı ikisinde de birebir aynı, ama
sonuç veri düzenine göre ayrışıyor — türetme "alan alan klonla" demenin kısa yolu, alanın
kendisinin ne anlama geldiğine karışmıyor.

Bu ayrım, elle yazılan bir gövdeyle türetilmiş bir gövde arasındaki farkı bir kez daha
gösteriyor: elle yazılan bir `impl Clone`, isterse `Bagli` için de bağımsız bir kopya
üretecek biçimde yazılabilirdi — içindeki `RefCell`in değerini okuyup yeni bir `Rc` kurarak.
Türetilmiş gövde bu seçeneği sunmuyor; her zaman "alanları olduğu gibi klonla" diyor, alanın
kendi `Clone` gerçekleştirimi ne yapıyorsa onu miras alıyor. Bir tipin klonunun paylaşımlı mı
bağımsız mı olacağına türetme değil, alan tiplerinin seçimi karar veriyor.

Türetmenin bir de sessiz bir önkoşulu var: bir yapı için `#[derive(PartialEq)]` yazmak,
yapının **her alanının** kendi başına `PartialEq`i gerçekleştirmiş olmasını gerektiriyor —
derleyici alan alan karşılaştırma üretirken her alan için `==` çağırıyor ve bu çağrının
derlenebilmesi için alanın kendi tipinin karşılaştırılabilir olması şart. `Nokta`nın `x` ve
`y` alanları `i32` olduğu, `i32` de `PartialEq`i zaten taşıdığı için türetme sorunsuz
derleniyor. Alanlardan biri karşılaştırılamayan bir tip taşısaydı (örneğin bir fonksiyon
işaretçisi), aynı `#[derive(PartialEq)]` satırı derleyici tarafından reddedilirdi — bu redd
de, kursun genel kuralı gereği, bir kod bloğuna konmuyor; yukarıdaki `Nokta` ve `ElleEsit`
örnekleri zaten kabul edilen karşılığı gösteriyor: yalnız karşılaştırılabilir alanlar
taşıyan yapılar.

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

Türetme her tipte aynı anlama gelmiyor. Aynı `#[derive(Clone)]` bir tipte paylaşımlı bir
kopya, başka bir tipte bağımsız bir kopya üretebiliyor; aynı `#[derive(PartialEq)]` bir
tipte iki alanı, başka bir tipte on alanı karşılaştırabiliyor — üretilen gövdenin **ne
yaptığı**, üzerine kurulduğu tipin alanlarına bağlı. Bu ders türetilmiş gövdenin kaç kez
koştuğunu sayamadığı gibi, iki farklı türetimin aynı işi yaptığını da varsaymıyor; yalnız
ikisinin de derleyici tarafından üretildiğini ve davranışının koşturularak gözlemlenebildiğini
gösteriyor. Kursun ölçüm yöntemi burada açık bir sınıra çarpıyor: kaynakta bir sayma çağrısı
yazılamayan her yerde, sayı yerine **davranış karşılaştırması** devreye giriyor.

Bu sınırın pratik bir sonucu var: türetilmiş bir gövdeye güvenmek, o gövdenin **tasarımını**
anlamayı gerektiriyor, çünkü çalışma zamanında onu izlemenin bir yolu yok. `Bagli` tipindeki
`Rc<RefCell<i32>>` alanı için `#[derive(Clone)]` yazmak, "bu tipin kopyası paylaşımlı olsun"
kararını **görünmez** biçimde alıyor — bu karar yapı bildirimini okuyan birine `derive`
satırından değil, alanın kendi tipinden belli oluyor. Elle yazılan bir `impl Clone` bu kararı
gövdenin içinde açıkça gösterebilirdi (bir referans mı kopyalanıyor, bir değer mi); türetilmiş
gövde bu kararı alan tipine devrediyor ve kaynakta ayrıca yazmıyor. Bu, türetmenin kısayolunun
bedeli: kaynak kısalıyor, ama kaynağı okuyanın davranışı anlamak için alan tiplerine kadar
inmesi gerekiyor.

Önceki dört ders, gövde her zaman kaynakta bir `fn` satırının altında durduğu için, koşum
sayısını `say` çağrısıyla doğrudan ölçebiliyordu. Bu ders o varsayımı kırıyor: gövde kaynakta
yok, ama davranış var ve doğrulanabilir. Kursun ölçüm yöntemi, sayamadığı yerde susmayı
seçiyor — bir sayı uydurmak yerine, "burada sayı yok" diyor.

#### Özet

- Türetme özniteliği (`#[derive(...)]`), bir trait'i elle yazmadan, derleyiciye ürettirerek
  gerçekleştirir; kaynakta bir yöntem gövdesi bırakmaz.
- Elle yazılan gövde (`elle-esitlik-govdesi`) her seferinde sınıfına girer ve sayılabilir —
  bu derste iki kez koştu; türetilmiş gövde her çağrıldığında çalışır ama kaynakta gövdesi
  olmadığı için bu koşum sayılamaz.
- Sayılamayan bir gövdenin davranışı yine de koşturularak gözlemlenebilir: doğru sonucu
  verip vermediği görülebilir, kaç kez çalıştığı görülemez.
- Aynı türetme iki farklı veri düzeninde iki farklı sonuç verebilir — `Rc<RefCell<...>>`
  alanı olan bir tipte paylaşımlı, düz bir sayısal alanda bağımsız bir kopya üretir.

#### Sonraki Adım

Bu ders, bir gövdenin **hiç yazılmamış olsa bile** çalıştığını gösterdi. Kursun son dersi
başka bir görünmezliğe bakıyor: bir işlecin (`+` gibi) çağrıldığı yer kaynakta hiç görünmez,
yalnız işlecin kendisi görünür. Sıradaki ders bu görünmez çağrının kaç kez koştuğunu, üç
terimli bir toplamı koşturarak sayıyor.

### İşleç Aşırı Yükleme

Source: https://academia.sh/tr/kurslar/rust-soyutlama/islec-asiri-yukleme

Bir işlecin (+ gibi) her kullanımı, arkasında görünmeyen bir trait çağrısıdır; üç terimli bir toplam iki kez çağırır, beş terimli bir toplam dört kez — işlecin kısa sözdizimi çağrı sayısını değiştirmez, yalnız çağrının kaynakta görünür olup olmadığını değiştirir ve bu sayı ancak koşum defteriyle bilinebilir.

Önceki ders, kaynakta hiç yazılı olmayan bir gövdenin yine de çalıştığını gösterdi. Bu ders
tam tersi bir görünmezliğe bakıyor: gövde kaynakta yazılı, ama onu **çağıran satır** kaynakta
görünmüyor. `a + b` yazıldığında bir çağrı yapılıyor, ama kaynakta `add` adını taşıyan hiçbir
satır yok — çağrı, `+` işlecinin arkasına gizlenmiş. Nesneye Dayalı Java kursu işleç aşırı
yüklemeyi zaten tanımlamıştı; orada ölçülen kararı hangi tipin verdiğiydi, burada ölçülen bir
işleç gövdesinin **kaç kez** koştuğu.

Bu kursun beş dersi boyunca ölçülen her gövde kaynakta bir adla anılabiliyordu: `ad`, bir
sınırlı fonksiyon, `uret`, `statik`/`dinamik`, `eq`. İşleç aşırı yüklemede bu adlandırma
kayboluyor — kaynakta yalnız `+` görünüyor, `add` sözcüğü hiçbir yerde geçmiyor. Bu son
ders, kursun ölçü ekseninin en görünmez uygulamasını taşıyor: bir gövdenin kaç kez koştuğunu
saymak için, önce o gövdeyi çağıran satırı kaynakta **tanımak** gerekiyor ve işleçlerde bu
tanıma adım kaynağın kendisinde değil, dilin sözdizimi kuralında.

#### İşleç Bir Trait Çağrısıdır

Rust'ta `+` işleci sabit bir davranış değil, `std::ops::Add` trait'inin bir çağrısı. Bir tip
`+` ile toplanabilir olsun istiyorsa, `Add`i gerçekleştirmesi gerekiyor; gerçekleştirmezse
`+` derlenmez. `a + b` yazımı, derleyici tarafından `Add::add(a, b)`e **açılıyor** — kaynakta
görünen kısayol, arkasında sıradan bir yöntem çağrısı taşıyor.

Bu açılma, önceki dersteki türetmeyle aynı kategoride değil. Türetilmiş bir gövde kaynakta
hiç yazılmıyordu; `add` gövdesi burada tamamen yazılı, `impl Add for Uzunluk` bloğunun
içinde satır satır duruyor. Görünmeyen şey gövde değil, gövdeyi **çağıran** satır — `+`
işareti bir çağrı taşıdığını söylemiyor, yalnız iki değeri yan yana koyuyor. Bu, temel
tamsayı ve kayan noktalı tipler için de aynı: `3 + 4` yazıldığında bile, arka planda aynı
kalıp işliyor, yalnız `i32` gibi ilkel tipler için bu çağrı derleyicinin kendi yerleşik
kurallarıyla, standart kitaplıktaki `Add` gerçekleştirimi üzerinden çözülüyor. `Uzunluk`
gibi kullanıcı tanımlı bir tip için aynı yolu açmak, tam olarak `Add`i gerçekleştirmek
anlamına geliyor.

```rust
// kere.rs — kesit: say/kere defteri, Uzunluk ve Add gerceklestirimi.

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ops::Add;
use std::rc::Rc;

pub type Defter = Rc<RefCell<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)
}

#[derive(Clone)]
pub struct Uzunluk {
    pub deger: i32,
    pub defter: Defter,
}

impl Add for Uzunluk {
    type Output = Uzunluk;
    fn add(self, oteki: Uzunluk) -> Uzunluk {
        say(&self.defter, "islec-govdesi");
        Uzunluk {
            deger: self.deger + oteki.deger,
            defter: Rc::clone(&self.defter),
        }
    }
}
```

`impl Add for Uzunluk` bloğu, önceki derste tanıdık bir yazımı taşıyor: `type Output =
Uzunluk;` satırı, İlişkili Tipler dersinde ele alınan ilişkili tip — `Add`in kendisi, toplamın
sonuç tipini kendi içinde bir tip olarak taşıyor. Bu ders `Output`a değil, `add` gövdesinin
kaç kez çağrıldığına bakıyor.

`add` yönteminin imzası da kendi başına bir gözlem taşıyor: `fn add(self, oteki: Uzunluk) ->
Uzunluk`. Parametre `self`in kendisi (`&self` değil) alınıyor — toplama işlemi iki `Uzunluk`
değerini **tüketiyor**, ödünç almıyor. Bu, Sahiplik ve Bellek Modeli kursunun sahiplik ve
taşıma dersinde kurulan kuralın burada tekrar karşımıza çıkması: `a + b` yazıldığında hem
`a` hem `b` bu ifadeden sonra kullanılamaz hale geliyor, çünkü ikisi de `add`in içine
taşındı. Yukarıdaki `a.clone() + b` yazımında `a`nın önce çoğaltılmasının nedeni tam bu —
`a` ikinci toplamda (`toplam + a`) tekrar kullanılacağı için, ilk toplamda tüketilmemesi
gerekiyor.

#### Üç Terim, İki Çağrı

```rust
// islec.rs — bir islecin her kullanimi bir cagridir; uc terimli toplam iki kez cagirir.
mod kere;
use kere::{kere, Defter, Uzunluk};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

fn olustur(defter: &Defter, deger: i32) -> Uzunluk {
    Uzunluk { deger, defter: Rc::clone(defter) }
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let a = olustur(&defter, 3);
    let b = olustur(&defter, 4);

    // Uc terim, iki '+' cagrisi: (a.clone() + b) + a
    let toplam = a.clone() + b;
    let toplam = toplam + a;
    println!(
        "uc-terim-toplam={} islec-govdesi={}",
        toplam.deger,
        kere(&defter, "islec-govdesi")
    );

    // Bes terim, dort '+' cagrisi - ayni ilkeyi ayri bir anahtarla dogruluyor.
    let defter2: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let bes: Vec<Uzunluk> = (1..=5).map(|d| olustur(&defter2, d)).collect();
    let mut uzun_toplam = bes[0].clone();
    for terim in bes.into_iter().skip(1) {
        uzun_toplam = uzun_toplam + terim;
    }
    println!(
        "bes-terim-toplam={} islec-govdesi={}",
        uzun_toplam.deger,
        kere(&defter2, "islec-govdesi")
    );
}
```

```
uc-terim-toplam=10 islec-govdesi=2
bes-terim-toplam=15 islec-govdesi=4
```

**TR12.** İlk ölçüm üç terimi ((`a`nın bir kopyası, `b`, `a`nın kendisi) topluyor ve
`islec-govdesi` iki kez koştu. Kaynakta `+` işareti de iki kez göründü — `a.clone() + b` ve
sonucun `a` ile toplanması — ve bu görünüm gövdenin kaç kez çağrıldığıyla birebir örtüşüyor. Bu
satır **her seferinde** sınıfına giriyor: işlecin her kullanımı, kaçınılmaz biçimde bir çağrıya
karşılık geliyor. İkinci ölçüm aynı ilkeyi ayrı bir defterle doğruluyor: beş terim toplanırken
kaynakta bir kez yazılan `+` dört kez koştu. İki ölçüm birlikte genel kuralı gösteriyor: **n
terimli bir toplamda işleç gövdesi n eksi bir kez koşar** — ilk terim hiçbir çağrıya ihtiyaç
duymadan zaten elde var, geri kalan her terim bir çağrı gerektiriyor.

Bu kural, toplamanın kendi yapısından geliyor: iki terim toplamak tek bir çağrı, üçüncü
terimi eklemek ikinci bir çağrı, dördüncüyü eklemek üçüncü bir çağrı. Beş terimli döngüde bu
birikim `bes[0]`den başlıyor — henüz hiçbir toplama yapılmadığı için sıfır çağrı — ve her
yeni terim bir çağrı ekleyerek dört çağrıda tamamlanıyor. Kaynaktaki döngü gövdesi tek bir
satır (`uzun_toplam = uzun_toplam + terim;`), ama bu satırın **kaç kez** yazıldığı değil,
**kaç kez çalıştığı** önemli — kaynakta bir kez yazılan bir satır, döngü aracılığıyla
kendini dört kez tekrar ediyor. Bu, İşleç Aşırı Yükleme dersinin taşıdığı ek bir gözlem: bir
çağrının kaynakta kaç kez **yazıldığı** ile koşumda kaç kez **çalıştığı** farklı sayılar
olabilir ve döngü bu ikisini ayrıştıran en yaygın araç.

Nesneye Dayalı Java kursu işleç aşırı yüklemenin **kararı hangi tipin verdiğini** ölçmüştü —
bir işlecin hangi gerçekleştirimin çağrılacağını işlenen değerlerin bildirilen tipinin mi
çalışma zamanı tipinin mi belirlediğini. Bu kurs o soruyu tekrar sormuyor; burada ölçülen,
zaten belirlenmiş bir gerçekleştirimin gövdesinin **kaç kez** koştuğu. Rust'ta bu belirleme
zaten derleme zamanında, `Uzunluk`in somut tipinden okunarak yapılıyor — kararın kendisi bu
dersin konusu değil.

#### Sözdizimi Kısayoldur, Çağrı Aynı

`+` yazımının bir kısayol olduğu, `Add::add`i açıkça çağırarak da doğrulanabilir — iki yazım
aynı çağrıya açılıyorsa, ikisinin de koşum defterine bıraktığı iz aynı olmalı.

```rust
// islec-esdeger.rs — '+' yazimi ile Add::add cagrisi ayni sayida kosuyor.
mod kere;
use kere::{kere, Defter, Uzunluk};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ops::Add;
use std::rc::Rc;

fn olustur(defter: &Defter, deger: i32) -> Uzunluk {
    Uzunluk { deger, defter: Rc::clone(defter) }
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let a = olustur(&defter, 5);
    let b = olustur(&defter, 6);

    // Iki yazim, ayni cagri: '+' iceren ve Add::add'i acikca cagiran.
    let sozdizimiyle = a.clone() + b.clone();
    let acikca = Add::add(a, b);

    println!("sozdizimiyle={} acikca={}", sozdizimiyle.deger, acikca.deger);
    println!("islec-govdesi={}", kere(&defter, "islec-govdesi"));
}
```

```
sozdizimiyle=11 acikca=11
islec-govdesi=2
```

**TR13.** `a.clone() + b.clone()` bir çağrı, `Add::add(a, b)` bir çağrı — ikisi de aynı
`add` gövdesini tetikliyor ve `islec-govdesi` iki kez koşuyor. Bu, `+` işaretinin
derleyiciye yeni bir davranış eklemediğini, yalnız zaten var olan bir çağrıyı kısaltarak
yazmanın bir yolu olduğunu doğruluyor. `Add::add(a, b)` yazımı, trait'in adını ve yöntemini
açıkça taşıyor; `a + b` aynı çağrıyı taşıyor ama adı gizliyor. İkisi arasında seçim yapmak
koşum sayısını hiç etkilemiyor, yalnız kaynağın **okunabilirliğini** etkiliyor.

`Add` tek başına değil; `std::ops` modülü her ikili işleç için benzer bir trait taşıyor —
çıkarma için `Sub`, çarpma için `Mul`, dizin erişimi için `Index`. Hepsi aynı ilkeye
dayanıyor: bir işaret, bir trait çağrısına açılıyor. Bu ders yalnız `Add`i ölçtü, ama sayım
yöntemi hepsi için aynı kalır: her kullanım bir çağrıdır ve zincirlendiklerinde çağrı
sayısı terim sayısından bir eksik olur.

Bu genelleme bir varsayım değil, `Add` ile aynı kalıbın diğer trait'lerde de tekrarlanmasından
geliyor: `Sub` `sub` adlı, `Mul` `mul` adlı, `Index` `index` adlı tek bir yöntem bildiriyor
ve her biri kendi işaretinin (`-`, `*`, `[]`) arkasına aynı biçimde gizleniyor. Bir tipin
birden fazla işleci aşırı yüklemesi de mümkün — `Uzunluk` isteseydi hem `Add` hem `Sub`
gerçekleştirebilirdi, ikisi de bağımsız `impl` bloklarında dururdu ve biri diğerinin koşum
sayısını etkilemezdi. Bu ders yalnız `Add`e odaklandı çünkü kursun ölçüm çekirdeği zaten onu
taşıyordu; ilke kalan işleçler için de aynı kalıyor.

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

İşleç gövdesi sıradan bir yöntemdir, ama **çağrıldığı yer kaynakta görünmez**. `a.clone() +
b` yazımında `add` sözcüğü hiçbir yerde geçmiyor — çağrı, `+` işaretinin arkasında gizli.
Bir okuyucu kaynağa bakarak "bu ifadede kaç çağrı var" sorusunu, işaretleri sayarak yanıtlar:
her `+` bir çağrıdır. Ama zincir uzadıkça, özellikle bir döngü ya da bir yineleyici zinciri
içinde üretildiğinde, kaynaktaki `+` işaretlerini saymak da yeterli olmayabilir — döngünün
kaç kez çalıştığı da bilinmesi gereken bir bilgidir. Yukarıdaki beş terimli döngüde kaynakta
tek bir `+` yazıyor (`uzun_toplam = uzun_toplam + terim;`), ama bu satır döngü içinde dört
kez çalıştığı için toplam çağrı sayısı dört. Kaynaktaki işaret sayısı ile çalışma zamanındaki
çağrı sayısı burada ayrışıyor: **kaç kez koştuğu ancak sayılarak, ancak koşum defteriyle
bilinebiliyor** — kaynağı okuyarak değil.

Bu, kursun baştan beri sorduğu sorunun son biçimi: kaynakta yazdığın bir gövde kaç kez koştu?
İşleç aşırı yüklemede bu soru en görünmez haliyle karşımıza çıkıyor, çünkü çağrının kendisi
bir ad taşımıyor — yalnız bir işaret. Kurs boyunca ölçülen on altı gövdenin sonuncusu da bu
işaretin arkasında duruyordu.

Bu görünmezlik, kursun ilk dersindeki bir gözlemi tersinden doğruluyor. Rust'ın Tasarım
Hedefleri dersi, `HashSet::insert` ile `Vec::push`in aynı işi (ekleme) yaptığını ama
imzalarına bakarak farklı sınıflara girdiğini göstermişti — sınıf, kaynaktan **okunarak**
belirlenebiliyordu. İşleç aşırı yüklemede ise sınıf (her seferinde) kaynaktan yine
okunabiliyor — `+` her zaman bir çağrıdır — ama çağrı **sayısı** kaynaktan okunamıyor,
çünkü kaynaktaki her `+` işareti bir döngü içindeyse birden fazla kez çalışabiliyor. Kurs
boyunca "sınıf kaynaktan okunur, sayı koşumdan gelir" ayrımı burada en net haliyle
görünüyor.

Bu altı ders, aynı soruyu altı farklı düzenekte sordu. Trait Tanımı ve Gerçekleştirimi,
kaynakta yazılı bir varsayılan gövdenin bir tip için koşup başka bir tip için hiç
koşmadığını gösterdi. Trait Sınırları, o gövdenin etrafına konan bir koşulun koşum sayısına
hiç dokunmadığını gösterdi. İlişkili Tipler, aynı sözleşmenin iki farklı yazımının koşum
sayısını eşit bıraktığını, farkın çağıranın yazdığında olduğunu gösterdi. Nesne Güvenliği ve
Dinamik Gönderim, çağrının derleme zamanında mı koşum zamanında mı çözüldüğünün de sayıyı
değiştirmediğini gösterdi. Türetilen Trait'ler, bir gövdenin kaynakta hiç yazılı olmadan da
koştuğunu, ama bu koşumun sayılamadığını gösterdi. Ve bu ders, yazılı bir gövdenin çağrısının
kaynakta görünmez olabileceğini gösterdi. Altısı birlikte, "kaç kez koştu" sorusunun
kaynaktaki görünürlükten bağımsız bir soru olduğunu ortaya koyuyor: bazen görünen gövde
koşmuyor, bazen görünmeyen bir çağrı koşuyor.

#### Özet

- Bir işlecin (`+` gibi) kullanımı, standart kitaplıktaki bir trait'in (`Add`) yöntem
  çağrısına karşılık gelir; kaynakta görünen kısayol arkasında sıradan bir çağrı taşır.
- İşleç gövdesi (`islec-govdesi`) her seferinde sınıfına girer: üç terimli bir toplamda iki
  kez, beş terimli bir toplamda dört kez koşar — çağrı sayısı her zaman terim sayısından bir
  eksiktir.
- İşlecin sözdizimsel kısalığı çağrı sayısını değiştirmez; `Uzunluk::add`i elle çağırmakla
  `+` yazmak aynı sayıda çalışır.
- Çağrı yeri kaynakta görünmediği için (yalnız bir işaret görünür), kaç kez koştuğu
  kaynağı okuyarak değil, ancak koşum defteriyle sayılarak bilinebilir.

Bu genel kural — n terim, n eksi bir çağrı — yalnız `Add` için değil, ikili işleçleri
zincirleyen her yazım için geçerli. Zincirin uzunluğu kaynakta bir kez yazılıp bir döngüyle
tekrarlanıyorsa (yukarıdaki beş terimli örnekte olduğu gibi), çağrı sayısını önceden
bilmenin tek yolu döngünün kaç kez çalışacağını bilmek; kaynaktaki işleç işaretlerini saymak
tek başına yeterli değil. Bu, kursun başından beri sürdürdüğü ayrımın son örneği: kaynak
**neyin** koşacağını söylüyor, koşum **kaç kez** koştuğunu söylüyor ve ikisi aynı bilgiyi
taşımıyor.

#### Sonraki Adım

Bu kurs, bir gövdenin ne zaman koştuğunu — her seferinde, yalnız gerektiğinde, hiç — sorarak
trait'leri, sınırları, ilişkili tipleri, gönderim biçimlerini, türetilmiş gövdeleri ve işleç
çağrılarını ölçtü. Sıradaki konu aynı soruyu bir gövdenin **başarısız olabileceği** yerlere
taşıyor: bir değerin yokluğu tipte nasıl ifade edilir ve o yokluğu karşılayan bir gövde
her zaman mı, yoksa yalnız gerektiğinde mi koşar?

## Hata Yönetimi

Yokluğun bir tiple ifade edilmesi, başarısızlığın dönüş değeri olarak taşınması, hata yayılımının tek bir işaretle kısaltılması, hataların kendi tiplerine dönüştürülüp zincirlenmesi, ve kurtarılamaz durumun tasarımda ayrı bir yere konması.

### Yokluğun Tiple İfadesi

Source: https://academia.sh/tr/kurslar/rust-soyutlama/yoklugun-tiple-ifadesi

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.

### Başarısızlığın Dönüş Değeri

Source: https://academia.sh/tr/kurslar/rust-soyutlama/basarisizligin-donus-degeri

Bir işlemin başarı ve başarısızlık yollarının kaynakta ayrı gövdeler olarak yazılması ve aynı işlev art arda çağrıldığında toplam çağrı sayısı sabit kalırken bu iki kolun kaç kez çalıştığının ayrışması.

`None` bir nedeni yoktur — bir değerin bulunmadığını söyler, ama neden bulunmadığını
söylemez. Bir dosya açma çağrısı başarısız olduğunda, çağıranın bilmesi gereken yalnız
"sonuç yok" değildir; dosyanın bulunamadığı mı, izin reddedildiği mi, yoksa başka bir
neden mi olduğu da gerekir. Bu ders, bir nedeni de taşıyan bir sonucun — `Result<T, E>`in
— kaç kez hangi kolu çalıştırdığını ölçüyor.

Bu konunun ilk dersinde ölçülen eksen (hazır mı tembel mi çalışıyor) burada geçerliliğini
korumuyor; `Ok` ve `Err` arasında "biri diğerinin daha ucuz biçimi" gibi bir ilişki yok.
Bunun yerine yeni bir soru devreye giriyor: kaynakta yan yana duran iki farklı gövdeden
hangisinin çalıştığı, girdiye göre nasıl değişiyor ve bu değişim koşum boyunca nasıl
birikiyor?

Arayüzler, Hatalar ve Genellikler kursu (M09/K02), bir hatanın M09'un üç bağ türünden
üçüncüsüne, *değerin içine*, yerleştiğini kurmuştu: hata ne bir bildirimde ne bir kullanım
yerinde, doğrudan dönüş değerinin kendisinde taşınıyordu. Bu ders o kararı miras alıyor —
`Result` da tam olarak bu düzeni izliyor — ama farklı bir soru soruyor: aynı işlev art
arda çağrıldığında, başarı ve başarısızlık kollarının her biri kaç kez çalışıyor?

Yokluğun Tiple İfadesi dersi, hazır ve tembel iki yazımın *aynı işi* yaptığını, yalnız
kaç kez çalıştıklarıyla ayrıştığını ölçmüştü. Bu derste ölçülen fark başka bir yerde
duruyor: `Ok` ve `Err` kolları aynı işi yapmıyor, tamamen farklı iki gövde çalıştırıyor.
Soru artık "aynı iş kaç kez koşuyor" değil, "hangi iş, ne sıklıkla koşuyor" — kaynakta
yazılan iki farklı gövdenin koşumdaki payı ne.

#### Bir Sonuç, İki Yol

`Result<T, E>`, önceki kursta kurulan numaralandırma düzeneğinin bir başka standart
kütüphane örneğidir: `Ok(T)` başarılı sonucu, `Err(E)` başarısızlığın nedenini taşır.
`Option`dan farkı, `None`un aksine `Err`in kendi başına bir değer taşımasıdır — başarısızlık
burada boş bir durum değil, kendi tipi olan bir veridir. `Option` "değer var mı" sorusuna
yanıt verir, `Result` "işlem neden başarısız oldu" sorusuna da yanıt verebilir.

**HT4.** Bu iki tip aynı numaralandırma düzeneğini kullanır, ama farklı bir bilgiyi taşırlar:
`Option`ın `None`u hiçbir veri taşımaz, `Result`ın `Err`i taşır. Bu, iki tipin birbirinin
yerine geçemeyeceği anlamına da gelir: bir arama sonucunun bulunup bulunmadığını
söylemek için `Option` yeterlidir, ama bir işlemin neden başarısız olduğunu söylemek
gerekiyorsa `Result` gerekir. Bir kod tabanında ikisi birlikte de görülür — örneğin bir
`Result<Option<T>, E>`, hem işlemin başarısız olabileceğini hem de başarılı olsa bile bir
değer bulamayabileceğini aynı anda taşır; bu birleşim bu derste ölçülmüyor, ama iki tipin
neden ayrı tutulduğunu netleştiriyor. Bir işlevin dönüş tipi
`Result<i32, SayiHatasi>` ise, çağıran taraf yalnız "başarısız mı oldu" diye sormaz, aynı
zamanda hangi hatanın oluştuğunu okuyabilir. Bu derste kullanılan `SayiHatasi`, hiçbir alan
taşımayan boş bir yapıdır — hatanın kendisi bu derste önemli değil, önemli olan hangi
kolun ne zaman çalıştığı. Özel Hata Tipleri dersi hatanın kendisine, taşıdığı bilgiye
odaklanacak.

Yukarıdaki `match`te `Err` kolu hatanın kendisini bir ada bağlamıyor, `Err(_)` yazıyor —
alt çizgi, Desen Eşleme dersinde (M10/K01) kurulan yakalayıcı dal (catch-all) deseninin bir
biçimi ve "bu değeri okumayacağım" demenin kaynaktaki karşılığı. `SayiHatasi` hiçbir alan
taşımadığı için burada okunacak bir şey de yok; sıradaki iki derste hata bir bilgi
taşımaya başlayınca, `Err(_)` yerini hatayı gerçekten bağlayan bir desene bırakacak.

`Option`ın `_or` / `_or_else` adlandırma deseni `Result` üzerinde de sürüyor:
`unwrap_or`, `unwrap_or_else`, `map_or`, `and_then` — hepsinin `Result` karşılığı var ve
hepsi aynı hazır/tembel ayrımını taşıyor. Ama bu ders o yöntemleri ölçmüyor; `Result`ın
kendine özgü olan tarafını, iki kolun **birbirinden farklı işler yaptığı** durumunu
ölçüyor. `unwrap_or` gibi bir yöntemde her iki "taraf" da aynı tipten aynı işi yapardı
(bir değer üretmek); burada `Ok` kolu bir hesaplamayı sürdürür, `Err` kolu hesaplamayı
durdurup bir neden üretir — ikisi farklı işler.

Bir işlevin `Result` döndürmesi, o işlevin kendi hatasını kendi içinde çözmesi gerektiği
anlamına gelmez. `cozumle` kendi `Err`ini üretiyor ve döndürüyor; çağıran taraf bu değeri
kullanmaya, görmezden gelmeye ya da kendi dönüş değerine olduğu gibi taşımaya karar
verebilir. Bu üçüncü seçenek — bir hatayı işlemeden yukarı taşımak — art arda birkaç
işlev sınırını geçebilir ve her geçişte kaynakta yeni bir kol yazmak gerekmez. Bu ders bu
taşımayı ölçmüyor; yalnız tek bir sınırda, tek bir `match` üzerinde, iki kolun kaç kez
çalıştığını sayıyor. Taşımanın kendisi Hata Yayılımının Kısa Yazımı dersinin konusu.

#### Kollardan Hangisi Çalışıyor

Bir işlev `Result` döndürdüğünde, çağıran taraf genellikle bir `match` ile iki kolu ayırır.
İki kol da sıradan Rust kodudur — biri diğerinden ayrı bir mekanizmayla çalışmaz, ikisi de
aynı işlevin dönüş değerine bakan aynı `match` ifadesinin bir parçasıdır. Bu, kolların
ikisinin de derlenmiş ikili dosyada yan yana durduğu, hangisinin çalışacağının yalnız
dönen değere (`Ok` mu `Err` mi) bağlı olduğu anlamına gelir. Aşağıdaki koşum aynı işlevi üç
farklı girdiyle çağırıyor ve her çağrıda hangi kolun çalıştığını sayıyor.

```rust
// hatakontrol.rs — basari ve basarisizlik kollarinin ayri govdeler olmasi
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)
}

#[derive(Debug)]
pub struct SayiHatasi;

pub fn cozumle(metin: &str) -> Result<i32, SayiHatasi> {
    match metin.parse::<i32>() {
        Ok(sayi) => Ok(sayi * 2),
        Err(_) => Err(SayiHatasi),
    }
}
```

```rust
// ikikol.rs — ayni islev uc kez cagriliyor, hangi kolun costugu sayiliyor
mod hatakontrol;
use hatakontrol::{Defter, say, kere, cozumle};
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::BTreeMap;

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

    for metin in ["8", "yirmibir", "15"] {
        match cozumle(metin) {
            Ok(sonuc) => {
                say(&defter, "basari-kolu");
                println!("{:<10} basari kolu, sonuc {}", metin, sonuc);
            }
            Err(_) => {
                say(&defter, "basarisizlik-kolu");
                println!("{:<10} basarisizlik kolu", metin);
            }
        }
    }

    println!();
    let toplam = kere(&defter, "basari-kolu") + kere(&defter, "basarisizlik-kolu");
    println!("toplam cagri: {}", toplam);
    println!("basari-kolu: {} kez", kere(&defter, "basari-kolu"));
    println!("basarisizlik-kolu: {} kez", kere(&defter, "basarisizlik-kolu"));
}
```

```
8          basari kolu, sonuc 16
yirmibir   basarisizlik kolu
15         basari kolu, sonuc 30

toplam cagri: 3
basari-kolu: 2 kez
basarisizlik-kolu: 1 kez
```

`cozumle` üç kez çağrılıyor ve toplam çağrı sayısı sabit: üç. Ama bu üç çağrının kolları
aynı biçimde dağılmıyor: iki çağrı `basari-kolu`na, bir çağrı `basarisizlik-kolu`na
düşüyor. **HT5.** Bu, kaynakta yazılan iki gövdenin — `Ok` kolunun gövdesi ve `Err` kolunun
gövdesi — birbirinden bağımsız çalıştığını gösteriyor: toplam sabit kalırken, hangi kolun
kaç kez seçildiği yalnız girdiye bağlı. `"yirmibir"` metni bir tam sayı olarak
ayrıştırılamadığı için `Err` kolu çalışıyor; `"8"` ve `"15"` ayrıştırılabildiği için `Ok`
kolu çalışıyor. Kaynakta iki kol da eşit uzunlukta yazılı, ama koşumda hangisinin ne kadar
çalıştığı girdiye bağlı bir sayı.

Bu ayrım, bir `Option`da görülenden farklı bir yerde duruyor. Önceki derste ölçülen fark
*hazır* ve *tembel* bir gövde arasındaydı — ikisi de aynı işi görebilirdi, farkları
çalışma zamanlamasıydı. Burada ise iki kol tamamen farklı işler yapıyor: biri sonucu
ikiye katlıyor, öbürü bir hata değeri üretiyor. Sayının kendisi bu farkı değiştirmiyor —
`kere` fonksiyonu hangi kolun ne iş yaptığını bilmiyor, yalnız kaç kez çağrıldığını
sayıyor. Ölçüm burada da aynı kalıyor: kaynakta yazılan bir gövdenin koşumda kaç kez
çalıştığı.

Bu üç girdi bir doğrulama işlem hattını temsil ediyor: bir kullanıcının girdiği üç ayrı
alan, sırayla ayrıştırılıyor. Gerçek bir doğrulama işlem hattında girdiler tek tek değil,
bir liste olarak gelir ve aynı `cozumle` çağrısı her alan için tekrarlanır. Yukarıdaki
`for` döngüsü bu düzeni küçük ölçekte gösteriyor: üç metin, aynı işlevden geçiyor, her biri
kendi kolunu seçiyor. Döngünün kendisi bir "koşum" değil — üç ayrı çağrının toplamı. Bu
ders için önemli olan, döngünün her adımının bağımsız bir çağrı olması ve her çağrının
kendi kolunu, önceki çağrılardan etkilenmeden seçmesi: ikinci çağrının `Err` dönmesi,
üçüncü çağrının hangi kolu seçeceğini hiçbir şekilde etkilemiyor. Sayaç defteri bu
bağımsızlığı doğal olarak koruyor — her `say` çağrısı yalnız kendi anahtarını artırıyor,
başka bir anahtara dokunmuyor.

`match` ifadesinin kendisi de Denetim Akışı dersinde kurulan ilkeyi taşıyor:
`match cozumle(metin) { ... }` bir ifadedir ve bir değer üretir — burada üretilen değer
`()`dir, çünkü her iki kol da yalnızca bir yan etki (sayaç kaydı ve yazdırma) üretip
görünürde bir değer döndürmüyor. Bu, `match`in kendisinin ifade olmasıyla, kollarının hangi
değeri ürettiği arasındaki farkı yeniden gösteriyor: Denetim Akışı dersi (M10/K01)
`match`in bir ifade olduğunu ve bütün dallarının aynı tipi vermesi gerektiğini kurmuştu;
burada bütün dallar `()` üretiyor, ama her dalın *içindeki* gövde birbirinden tamamen
farklı. Bir ifadenin kendisi tek bir değer üretse de, o değere varmak için çalışan kod dal
dal ayrışabilir.

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

İki kol da kaynakta yazılıdır, ama bir çağrıda yalnız biri çalışır. Aşağıdaki koşum tek
bir çağrıyla bunu somutlaştırıyor.

```rust
// tekcagri.rs — iki kol da kaynakta yazili, ama tek cagride yalniz biri kosuyor
mod hatakontrol;
use hatakontrol::{Defter, say, kere, cozumle};
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::BTreeMap;

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

    match cozumle("100") {
        Ok(sonuc) => {
            say(&defter, "basari-kolu");
            println!("sonuc: {}", sonuc);
        }
        Err(_) => {
            say(&defter, "basarisizlik-kolu");
            println!("hata olustu");
        }
    }

    println!("basari-kolu: {} kez", kere(&defter, "basari-kolu"));
    println!("basarisizlik-kolu: {} kez", kere(&defter, "basarisizlik-kolu"));
}
```

```
sonuc: 200
basari-kolu: 1 kez
basarisizlik-kolu: 0 kez
```

**HT6.** `cozumle("100")` çağrısı `Ok` sonucu verdiği için yalnız `basari-kolu` çalışıyor;
`basarisizlik-kolu` sıfır kalıyor. Ama bu sıfır, `Err` kolunun *var olmadığı* anlamına
gelmiyor — kod ikili dosyasında o kol duruyor, derleyici onu üretti, `match` bütünlük
gereği onu kapsamak zorunda kaldı. Yalnız bu belirli koşumda hiç seçilmedi. Bir kolun
kaynakta yazılı olması ile bir koşumda çalışmış olması iki ayrı olgu: biri kaynak kodun
bir özelliği, öbürü belirli bir girdiyle yapılan belirli bir koşumun sonucu. "Kapsanmış
kod" — derleyicinin kabul ettiği, bütünlük ilkesini sağlayan kod — ile "koşmuş kod" —
belirli bir çalıştırmada gerçekten işlemci tarafından yürütülen kod — arasındaki bu ayrım,
bir programın kaynağını okumakla bir koşumunu ölçmek arasındaki farkın da özeti.

Bu ayrımın önemi, bütünlük ilkesinin ne sağladığını düşününce daha net görünüyor. Bütünlük
ilkesi olmasaydı — yani `match` yalnız `Ok` kolunu yazıp `Err` durumunu görmezden gelmeye
izin verseydi — kaynakta `basarisizlik-kolu` diye bir gövde hiç bulunmayabilirdi ve
`"yirmibir"` gibi bir girdi geldiğinde program tanımsız bir duruma düşerdi: ne bir hata
değeri üretilirdi ne de akış nereye gideceğini bilirdi. Bütünlük ilkesi bunu engelliyor —
`Err` kolu kaynakta bulunmak *zorunda*, ister o koşumda hiç çalışmasın ister koşumun
tamamını devralsın. Sıfır kez çalışmış olması, orada gerekli olmadığı anlamına gelmiyor;
tam tersine, sıfırın ölçülebilmesinin nedeni, kolun zaten orada, çalışmaya hazır durmasıdır.

Bu fark küçük bir örnekte belirgin görünmeyebilir, ama bir işlevin çağrıldığı yer sayısı
büyüdükçe önem kazanıyor. Bir işlev binlerce kez çağrılıyorsa ve girdilerin çoğu geçerliyse,
`Err` kolu kaynakta yer kaplar, derlenir, doğrulanır — ama koşumda neredeyse hiç
çalışmayabilir. Tam tersi de mümkün: bir işlev çoğunlukla geçersiz girdilerle çağrılıyorsa,
`Ok` kolu kaynakta dursa da koşumda ender çalışır. Kaynağın kendisi hangi kolun daha sık
çalışacağını söylemez; bunu yalnız gerçek bir koşum söyler.

Yukarıdaki ikinci koşumda üç girdiden ikisi başarılı, biri başarısızdı — `basari-kolu` iki,
`basarisizlik-kolu` bir kez çalıştı. Aynı işlev bin girdiyle çağrılsaydı ve girdilerin
yalnız birkaçı geçersiz olsaydı, oran da buna göre kayardı: `basari-kolu` binin yakınında,
`basarisizlik-kolu` tek haneli bir sayıda kalabilirdi. Kaynakta yazılan iki kolun uzunluğu
bu oranla ilgisizdir — `Err` kolu tek satırlık bir gövde olabilir ve yine de koşumun
neredeyse tamamını devralabilir, girdiler öyle gerektiriyorsa. Bu ders bu oranı kestirmeye
çalışmıyor; yalnız oranın kaynaktan değil, koşumdan okunması gerektiğini gösteriyor.

Yukarıdaki iki koşum birlikte, bu konunun açılışında sorulan soruyu kısmen yanıtlıyor:
`Option`da yedek gövdenin çalışıp çalışmadığı *hazır mı tembel mi* yazıldığına bağlıydı;
`Result`da bir kolun çalışıp çalışmadığı, o kolun hangi girdiye karşılık geldiğine bağlı.
İkisi de aynı ölçüm çerçevesine — kaynakta yazılan bir gövdenin koşumda kaç kez çalıştığı —
oturuyor, ama her biri kendi nedenine göre "her seferinde", "yalnız gerektiğinde" ya da
"hiç" sınıfına giriyor.

#### Özet

- `Result<T, E>`, `Option`ın aksine başarısızlığın *nedenini* de taşır: `Ok(T)` ve
  `Err(E)` ayrı bilgiler içerir; `Option`ın `_or` / `_or_else` adlandırma deseni burada da
  geçerlidir.
- Başarı ve başarısızlık kollarının gövdeleri kaynakta ayrı yazılır ve birbirinden
  bağımsız çalışır; aynı işlev art arda çağrıldığında **toplam çağrı sayısı sabit** kalır,
  ama kol sayıları girdiye göre ayrışır.
- Bir kolun kaynakta yazılı olması onun her koşumda çalıştığı anlamına gelmez — tek bir
  çağrıda iki kolun yalnız biri çalışır, öbürü o koşumda **hiç** çalışmaz.
- "Kapsanmış kod" (derleyicinin kabul ettiği, bütünlük gereği yazılmış kod) ile "koşmuş
  kod" (belirli bir koşumda gerçekten çalışan kod) iki ayrı olgudur.
- `Err(_)` gibi bir yakalayıcı desen, hatanın içeriğini okumadan yalnız hangi kolun
  çalıştığını saymaya yeter; hatanın taşıdığı bilgi sıradaki derslerin konusudur.
- Hangi kolun ne sıklıkla çalışacağı kaynaktan okunamaz; yalnız gerçek girdilerle yapılan
  bir koşumdan okunabilir. Bu koşumda iki kol da **yalnız gerektiğinde** çalıştı: her çağrı
  kendi girdisinin gerektirdiği kolu koşturdu, öbürü o çağrıda **hiç** koşmadı.

#### Sonraki Adım

Yukarıdaki her iki koşumda da başarı kolu aynı üç satırı tekrarlıyordu: değeri ayrıştır,
hata varsa bildir, yoksa devam et. Bu düzen, art arda birkaç ayrıştırma yapan bir işlevde
hızla tekrar eden bir kalıba dönüşür — özellikle bir işlev kendi içinde başka bir
ayrıştırma çağırıp onun sonucunu da denetlemek zorunda kaldığında, aynı `match` iskeleti
birkaç kez art arda yazılır. Hata Yayılımının Kısa Yazımı dersi, bu kalıbı tek bir
işaretle kısaltan bir yazımı ölçüyor — ve o kısaltmanın kaynağı kısalttığını, ama koşumu
değiştirmediğini, yani `basari-kolu` ile `basarisizlik-kolu`nun bu derste görülenle aynı
sayıları verdiğini gösteriyor. Kısa yazım, ölçülen sayıyı değil, o sayıya varmak için
yazılması gereken satır sayısını azaltıyor — bu ders yazılan `match` iskeletini elle
kurdu, sıradaki ders aynı iskeletin kaynakta ne kadar kısalabildiğini, koşumu
değiştirmeden, ölçüyor.

### Hata Yayılımının Kısa Yazımı

Source: https://academia.sh/tr/kurslar/rust-soyutlama/hata-yayiliminin-kisa-yazimi

Bir hatayı denetleyip erken dönmeyi tek bir işarete indirgeyen kısa yazımın, aynı işi elle yazılan bir dallanmayla birebir aynı sayıları verdiğinin — kaynağı kısalttığının, koşumu değiştirmediğinin — ölçülmesi.

Başarısızlığın Dönüş Değeri dersi, aynı işlevin art arda çağrıldığında başarı ve
başarısızlık kollarının birbirinden bağımsız çalıştığını, her ikisinin de aynı `match`
gövdesinde üç satırlık bir düzeni tekrarladığını gösterdi: değeri ayrıştır, hata varsa
bildir, yoksa devam et. Bir işlev birden çok ayrıştırma yapıyorsa bu düzen aynı biçimde art
arda tekrarlanır — her ayrıştırmadan sonra aynı iki kollu `match`. Bu ders, bu tekrarı tek
bir işarete indirgeyen kısa yazımı ölçüyor: soru işareti (`?`).

Bu konunun üçüncü dersi olarak, bu ders artık başarı ve başarısızlığın ayrı gövdeler
olduğunu varsayıyor — o ayrım önceki derste kuruldu. Burada sorulan soru bir adım öteye
geçiyor: bu iki gövdeyi ayıran `match` iskeleti, kaynakta ne kadar kısalabilir ve bu
kısalma koşumdaki sayıları değiştirir mi? Yanıt, aşağıdaki iki koşumun karşılaştırılmasıyla
ortaya çıkıyor.

Arayüzler, Hatalar ve Genellikler kursu (M09/K02) bir hatanın bir dönüş değeri olarak
taşındığını, kontrol akışının dışına çıkmadığını kurmuştu. Soru işareti de bu kararı
bozmuyor — hâlâ bir dönüş değeri üretiyor, hâlâ normal fonksiyon dönüşü kullanıyor — yalnız
o dönüşü yazmanın kaynaktaki uzunluğunu kısaltıyor. Bu ders M09/K02'nin ölçtüğü *nerede*
sorusuna dönmüyor; ölçtüğü, kısa yazımın koşumu değiştirip değiştirmediği.

Fonksiyonlar ve İfade Odaklılık dersi (M10/K01), bir işlevin gövdesi bitmeden erken
dönmenin ne zaman gerektiğini ölçmüştü: gövdenin geri kalanı hesaplanmayacaksa, açık bir
`return` bu durumu kaynakta görünür kılıyordu. Soru işareti tam olarak bu erken dönüşü
kullanıyor — yalnız `return` yazımını elle yazmak yerine, bir `Result` çağrısının hemen
ardına tek bir işaret koyarak. Bu ders o dersin kurduğu erken dönüş kavramını yeniden
kurmuyor; yalnız bu belirli kısa yazımın, elle yazılan bir erken dönüşle *aynı sayıları*
verip vermediğini ölçüyor.

#### Aynı Sayılar, Daha Kısa Kaynak

Soru işareti, bir `Result` döndüren bir işlevin içinde, başka bir `Result` döndüren bir
çağrının hemen ardına yazılır. Çağrı `Ok` dönerse, içindeki değer açılır ve gövde kaldığı
yerden devam eder; `Err` dönerse, işlev o anda kendi `Err`iyle döner ve gövdenin geri kalanı
hiç çalışmaz.

Bu kısayolun bir koşulu var: soru işareti yalnız, içinde bulunduğu işlevin dönüş tipi de
bir `Result` (ya da `Option`) olduğunda yazılabilir. `main` işlevinin dönüş tipi `()` ise,
`main`in gövdesinde bir `Result` üreten bir çağrının ardına doğrudan `?` konamaz — erken
dönüşün döneceği bir `Result` yuvası yok. Bu ders bu koşulu ihlal etmiyor: `cozumle` kendi
`Result` dönüş tipini taşıyor, `?` yalnız onun gövdesinde kullanılıyor; `main` içindeki
çağrılar ise dönen `Result`ı bir değişkene bağlayıp `{:?}` ile yazdırıyor, kendisi `?`
kullanmıyor.

Aşağıdaki `cozumle` işlevinde `metin.parse()` bir `Result<i32, ParseIntError>` döndürür,
ama işlevin kendi dönüş tipi `Result<i32, SayiHatasi>`dir — iki farklı hata tipi. Soru
işareti bu farkı kendi başına çözer: `SayiHatasi` için yazılan `impl From<ParseIntError>`
gerçekleştirimi sayesinde, `parse`in ürettiği hata otomatik olarak `SayiHatasi`e
dönüştürülür. Bu dönüşümün kendisi Özel Hata Tipleri dersinin konusu; bu derste yalnız
`?`nin bu dönüşümü *kullandığı*, ama bu derste ölçülen sayıyı etkilemediği görülüyor.

```rust
// sorulu.rs — kere.rs'in cozumle kesiti: basarili columdan sonrasi bir kez, basarisizda hic kosuyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

fn say(defter: &Defter, ad: &str) {
    *defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}
fn kere(defter: &Defter, ad: &str) -> u32 {
    *defter.borrow().get(ad).unwrap_or(&0)
}

#[derive(Debug)]
struct SayiHatasi;

impl From<std::num::ParseIntError> for SayiHatasi {
    fn from(_: std::num::ParseIntError) -> SayiHatasi {
        SayiHatasi
    }
}

fn cozumle(defter: &Defter, metin: &str, etiket: &str) -> Result<i32, SayiHatasi> {
    let sayi: i32 = metin.parse()?;
    say(defter, etiket);
    Ok(sayi * 2)
}

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

    let basarili = cozumle(&defter, "21", "soru-sonrasi-basarili");
    let basarisiz = cozumle(&defter, "yirmibir", "soru-sonrasi-basarisiz");

    println!("basarili cagri: {:?}", basarili);
    println!("basarisiz cagri: {:?}", basarisiz);
    println!();
    for ad in ["soru-sonrasi-basarili", "soru-sonrasi-basarisiz"] {
        println!("{:<24}{} kez", ad, kere(&defter, ad));
    }
}
```

```
basarili cagri: Ok(42)
basarisiz cagri: Err(SayiHatasi)

soru-sonrasi-basarili   1 kez
soru-sonrasi-basarisiz  0 kez
```

**HT7.** `cozumle(&defter, "21", ...)` çağrısında `metin.parse()` başarıyla `21`i üretiyor;
soru işareti bu değeri açıyor, `sayi`ya bağlıyor ve gövde kaldığı yerden devam ediyor —
`say` çağrısı çalışıyor, satır `soru-sonrasi-basarili` bir kez artıyor. `cozumle(&defter,
"yirmibir", ...)` çağrısında ise `parse` bir hata üretiyor; soru işareti bu hatayı
`SayiHatasi`e çeviriyor ve işlevden hemen dönüyor. `say` çağrısına hiç sıra gelmiyor —
`soru-sonrasi-basarisiz` sıfırda kalıyor. Bu, önceki dersteki `Ok`/`Err` kollarının aynı
davranışı: biri çalışıyor, öbürü çalışmıyor. Fark, bu iki kolun artık ayrı yazılmış bir
`match` içinde değil, tek bir işaretin arkasında saklı olması.

`cozumle` işlevinin dönüş tipine bakıldığında, gövdesinde yalnız bir satır (`let sayi: i32
= metin.parse()?;`) `Result` ile ilgili görünüyor; geri kalan iki satır (`say` çağrısı ve
`Ok(sayi * 2)`) sıradan kod gibi okunuyor. Bu, soru işaretinin kaynakta bıraktığı izin ne
kadar küçük olduğunu gösteriyor — bir işlev, kaç ayrı ayrıştırma yapıyorsa yapsın, her
ayrıştırmanın ardına tek bir karakter ekleyerek hata denetimini tamamlıyor. `etiket`
parametresinin `say`e geçirilen adı taşıması, bu derste iki farklı çağrıyı (başarılı ve
başarısız) aynı işlevin üzerinden, ama ayrı sayaç anahtarlarıyla izlemeyi sağlıyor; gerçek
bir kullanımda bu parametre bulunmaz, yalnız ölçüm amacıyla eklendi.

Aynı işlev elle yazılan bir dallanmayla da kurulabilir. Aşağıdaki koşum, soru işaretini
`match` ve erken `return` ile değiştiriyor ve aynı iki girdiyle çağrılıyor.

```rust
// elle_dallanma.rs — ayni is elle dallanmayla yazilinca da ayni sayilari veriyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

fn say(defter: &Defter, ad: &str) {
    *defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}
fn kere(defter: &Defter, ad: &str) -> u32 {
    *defter.borrow().get(ad).unwrap_or(&0)
}

#[derive(Debug)]
struct SayiHatasi;

fn cozumle_elle(defter: &Defter, metin: &str, etiket: &str) -> Result<i32, SayiHatasi> {
    let ayristirma = metin.parse::<i32>();
    let sayi = match ayristirma {
        Ok(deger) => deger,
        Err(_) => return Err(SayiHatasi),
    };
    say(defter, etiket);
    Ok(sayi * 2)
}

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

    let basarili = cozumle_elle(&defter, "21", "soru-sonrasi-basarili");
    let basarisiz = cozumle_elle(&defter, "yirmibir", "soru-sonrasi-basarisiz");

    println!("basarili cagri: {:?}", basarili);
    println!("basarisiz cagri: {:?}", basarisiz);
    println!();
    for ad in ["soru-sonrasi-basarili", "soru-sonrasi-basarisiz"] {
        println!("{:<24}{} kez", ad, kere(&defter, ad));
    }
}
```

```
basarili cagri: Ok(42)
basarisiz cagri: Err(SayiHatasi)

soru-sonrasi-basarili   1 kez
soru-sonrasi-basarisiz  0 kez
```

**HT8.** İki koşum birebir aynı sayıları veriyor: `soru-sonrasi-basarili` bir, `soru-sonrasi-basarisiz`
sıfır. Kaynak farklı — ilkinde tek satır (`metin.parse()?`), ikincisinde beş satırlık bir
`match` — ama koşum aynı. Soru işareti yeni bir davranış eklemiyor; elle yazılan `match` ve
erken `return`ün derleyicinin ürettiği koduyla aynı işi, daha az yazarak yapıyor. Bu, kısa
yazımın *koşumu* değil *kaynağı* kısalttığının doğrudan kanıtı: iki program farklı uzunlukta
kaynağa sahip, ama aynı ikili davranışı üretiyor.

`cozumle_elle` işlevindeki `match`, önceki dersteki `match`ten bir farkla ayrılıyor: `Err`
kolu bir değer üretip gövdenin geri kalanına devretmiyor, doğrudan `return Err(SayiHatasi)`
ile işlevden çıkıyor. Bu, Denetim Akışı dersinde (M10/K01) kurulan `match`in bir ifade
olması ilkesiyle çelişmiyor — `return` bir ifade değil bir deyim ve bir `match` kolunun
içinde bir deyim yazılabilir; o kolun ürettiği "değer" gövdenin geri kalanına hiç ulaşmıyor,
çünkü işlevin kendisinden çıkılıyor. `Ok` kolu ise sıradan bir değer (`deger`) üretiyor ve
bu değer `sayi`ya bağlanıyor, gövde oradan devam ediyor. İki kolun farklı doğası —
biri işlevden çıkıyor, öbürü bir değer veriyor — soru işaretinin arkasında da aynen
duruyor, yalnız görünmez.

Kaynaktaki kısalmanın gerçek bedeli, bir işlev birden çok ayrıştırma yaptığında görülür.
Üç ayrı alanı sırayla ayrıştıran bir işlev, elle yazılan dallanmayla üç kez tekrarlanan
beş satırlık bir `match` (toplam on beş satır) gerektirirdi; aynı işlev soru işaretiyle üç
satıra iner — her alanın ardına tek bir işaret. Bu derste ölçülen iki koşum (bir satırlık
kısa yazım, beş satırlık elle yazım) bu farkın en küçük örneği; alan sayısı arttıkça
kaynaktaki fark da büyür, ama her iki yazımın da ürettiği `soru-sonrasi-basarili` /
`soru-sonrasi-basarisiz` çifti aynı kalır. Kısa yazımın büyümesi doğrusal değil — kaynak
satır sayısı azalırken, koşumun kendisi hiç değişmez.

Soru işareti `Option` üzerinde de aynı biçimde çalışır: içinde bulunduğu işlev `Option`
dönüyorsa, `Some` içindeki değeri açar, `None` durumunda işlevden hemen `None` döndürür. Bu
ders bu ikinci kullanımı koşturmuyor — Yokluğun Tiple İfadesi dersinde ölçülen `Option`
yöntemleri bir değer üretme (`unwrap_or`, `unwrap_or_else`) üzerine kuruluydu, burada
ölçülen ise erken dönüş. İki kullanım da aynı işareti taşısa da, biri bir değer üretir
(`unwrap_or_else`in yaptığı gibi), öbürü işlevden çıkar; bu derste ölçülen yalnız
`Result` üzerindeki, işlevden çıkan biçimi.

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

Yukarıdaki koşumda `soru-sonrasi-basarili` ve `soru-sonrasi-basarisiz` her zaman birlikte
hareket ediyor: biri bir olduğunda öbürü sıfır, biri sıfır olduğunda öbürü bir. Bu, tek bir
gözlemin değil, aynı satırın iki farklı çağrıdaki durumunun sonucu — ikisi de aslında aynı
`say(defter, etiket)` çağrısını temsil ediyor, yalnız `etiket` argümanı çağırana göre
değişiyor.

**HT9.** Erken dönüş, kendisinden sonraki gövdeyi sessizce atlıyor — ne bir uyarı yazdırıyor
ne bir işaret bırakıyor. `"yirmibir"` girdisiyle yapılan çağrıda `say` çağrısı hiç
çalışmadığı için sayaç defterine hiçbir kayıt düşmüyor; bu, "adım atlandı" diye bir iz
bırakmıyor, yalnız beklenen kaydın *yokluğuyla* görünüyor. Atlanan adımın kendisi bir yan
etki üretmiş olsaydı — bir dosyaya yazma, bir sayacı artırma, bir günlük satırı basma — o
yan etki de gerçekleşmezdi. Bu ders bunu tam olarak `say` çağrısının kendisiyle gösteriyor:
`say`, atlanan adımın *tek* yan etkisi ve o yan etki gerçekleşmediği için sayı sıfırda
kalıyor.

Bu, önceki dersteki `Err` kolunun kaynakta yazılı ama koşmamış olmasından farklı bir
durum. Orada iki kol da aynı `match`in içinde açıkça duruyordu, ikisi de görünürdü. Burada
"atlanan gövde" ayrı bir kol olarak yazılmıyor — soru işaretinden sonraki her satır, hata
durumunda örtük olarak atlanıyor. Kaynağı okuyan biri, `?`den sonraki satırların hangi
koşulda çalışacağını görebilir, ama derleyicinin bunun için ayrı bir kol ürettiğini
kaynakta görmez; bu kol yalnız koşumda, sayaçla ölçüldüğünde ortaya çıkıyor.

Bu sessizlik bir tasarım kararının sonucu, bir kusur değil. Soru işareti bir uyarı
yazdırmıyor, çünkü hatanın kendisi zaten dönüş değeri olarak taşınıyor — çağıran taraf bu
değeri denetlediği sürece, "neden atlandığı" bilgisi kayıp değil, yalnız gövdenin geri
kalanında değil, dönüş değerinde duruyor. Bu ders bu bilgiyi izlemiyor; yalnız gövdenin geri
kalanının çalışıp çalışmadığını sayıyor. Bir işlevin `cozumle`yi çağıran tarafı, dönen
`Result`ı denetlemezse, hem bu bilgiyi hem de neyin atlandığını görme fırsatını kaybeder —
ama bu, soru işaretinin değil, çağıran tarafın denetlememe kararının sonucu.

Aynı sessiz atlama, işlev sınırlarını art arda geçen bir soru işareti zincirinde de aynı
biçimde tekrarlanır. Bir işlev kendi içinde soru işaretiyle başka bir işlevi çağırıyorsa
ve o işlev de kendi içinde bir üçüncü işlevi soru işaretiyle çağırıyorsa, en alttaki
başarısızlık üç ayrı gövdeyi de sessizce atlayarak en üste kadar yükselir — hiçbiri bir iz
bırakmadan. Bu ders bu çok katmanlı zinciri koşturmuyor; yalnız tek bir sınırda, atlamanın
kendisinin sessiz olduğunu gösteriyor.

Bu üç katmanlı zinciri düşünmek sınırı daha da netleştiriyor. En alttaki işlev bir
ayrıştırma yapıyor ve başarısız oluyor diyelim; onu çağıran orta katman kendi gövdesinde
üç satır daha çalıştıracaktı, ama hiç çalıştırmıyor; en üstteki çağıran ise beş satır daha
çalıştıracaktı, o da çalıştırmıyor. Toplamda sekiz satırlık gövde, tek bir başarısızlık
yüzünden sessizce atlanmış oluyor — ama kaynakta bu sekiz satırın "atlanabilir" olduğunu
işaretleyen hiçbir yorum, hiçbir özel söz dizimi yok. Yalnız her üç katmanın da kendi
dönüş tipinin `Result` olması ve her katmanın çağrısının ardında bir soru işareti
bulunması, bu sessiz atlamanın mümkün olduğunu gösteriyor.

Bu konunun ilk iki dersinde ölçülen ayrım burada da geçerliliğini koruyor: `Option`da hazır
ve tembel gövdeler arasındaki fark, `Result`da başarı ve başarısızlık kolları arasındaki
fark, burada da aynı biçimde duruyor — yalnız artık `match` kolları elle yazılmıyor, tek
bir işaretin ardında saklanıyor. Kısa yazım, ölçülen üç sınıfın (her seferinde, yalnız
gerektiğinde, hiç) hiçbirini ortadan kaldırmıyor; yalnız o sınıfları taşıyan kaynağı
küçültüyor.

#### Özet

- Soru işareti, bir `Result` çağrısının ardından yazılan erken dönüşü tek bir işarete
  indirger; `Ok` dönerse değeri açar ve gövde devam eder, `Err` dönerse işlev hemen döner.
- Soru işaretinden sonraki gövde, çağrı başarılı olduğunda **yalnız gerektiğinde** çalışır
  — çağrı başarısız olduğunda **hiç** çalışmaz.
- Aynı işlev elle yazılan bir `match` ve erken `return` ile kurulduğunda **aynı sayıları**
  verir: kısa yazım koşumu değil, kaynağı kısaltır — bu fark, bir işlev birden çok
  ayrıştırma yaptıkça büyür.
- Erken dönüş kalan gövdeyi sessizce atlar; atlanan adımın hiçbir yan etkisi de
  gerçekleşmez — ne bir uyarı, ne bir kısmi kayıt.
- Atlanan gövde kaynakta ayrı bir kol olarak görünmez; yalnız sayaçla ölçüldüğünde,
  eksik kalan kayıtla fark edilir.
- Soru işareti farklı hata tiplerini `From` ile otomatik dönüştürür; bu dönüşümün kim
  tarafından çağrıldığı sıradaki dersin konusudur.

#### Sonraki Adım

Yukarıdaki `SayiHatasi`, `std::num::ParseIntError`i kendi tipine dönüştüren bir `From`
gerçekleştirimi taşıyordu — soru işaretinin bu dönüşümü nasıl çağırdığı bu derste
gösterilmedi, yalnız kullanıldı. `cozumle` işlevinin gövdesinde bu dönüşümü çağıran açık
bir satır yok; `metin.parse()?` yazımının kendisi hem hatayı denetliyor hem de gerekirse
dönüştürüyor. Özel Hata Tipleri dersi bu dönüşümü kimin çağırdığını ölçüyor: kısa yazım bu
çağrıyı çağıran taraf hiçbir şey yazmadan mı yapıyor, yoksa elle yazılan bir dönüşüm mü
gerekiyor — ve iki yaklaşım aynı bilgiyi taşıyabiliyor mu, yoksa örtük dönüşüm bir şeyi
kaybediyor mu?

### Özel Hata Tipleri

Source: https://academia.sh/tr/kurslar/rust-soyutlama/ozel-hata-tipleri

Soru işaretinin, çağıran taraf hiçbir dönüşüm çağrısı yazmadan bir hata tipini başka bir hata tipine çevirdiğinin ve bu örtük dönüşümün — elle yazılan bir dönüşümün aksine — çağıran taraftaki bağlam bilgisini taşıyamadığının ölçülmesi.

Hata Yayılımının Kısa Yazımı dersi, soru işaretinin farklı hata tiplerini birbirine
dönüştürdüğünü gösterdi, ama bu dönüşümün *nasıl* gerçekleştiğine girmedi — yalnız
kullandı. Bu ders o dönüşümü açıyor: bir hata bir tipten başka bir tipe geçerken, bu
geçişi kim çağırıyor ve bu çağrı çağıran tarafın kaynağında görünür mü?

Önceki derste kurulan `impl From<std::num::ParseIntError> for SayiHatasi` bloğu bu
sorunun kilidini açan parça. O ders bu bloğu yalnız soru işaretinin çalışabilmesi için
gerekli bir ön koşul olarak kullandı; bu ders aynı bloğun *kendisini* inceliyor — içinde
ne kuruluyor, ne atılıyor ve bu kurma işlemi kim tarafından, kaç kez tetikleniyor.

Arayüzler, Hatalar ve Genellikler kursu (M09/K02) hata sarmalamayı (`error wrapping`) ve
hata zincirini ölçmüştü — bir hatanın başka bir hatayı içinde taşıyıp taşımadığını, hata
kimliğinin zincirde nasıl korunduğunu. Bu ders sarmalama değil **dönüşüm** ölçüyor: bir
hata tipi, kendi hâlini koruyarak değil, tamamen başka bir tipe çevrilerek ilerliyor. Rust
bu iki düzeneği ayrı tutar — sarmalama M09/K02'nin konusuydu, dönüşüm burada.

Sarmalama, bir hatayı bir zarfa koyar; zarfın içinde orijinal hata bozulmadan durur ve
gerektiğinde geri çıkarılabilir. Dönüşüm ise orijinal hatayı bozar: kaynak tip elden
çıkar, yerine yeni bir tip gelir ve o yeni tip artık eskisine dönemez — meğerki eskisini
kendi içinde bir alan olarak sakladı. Bu ders bu ikinci düzeneği, `SayiHatasi`nin
`ParseIntError`i kendi içinde saklamadığı, yalnız onu bir sinyale indirgediği bir örnekle
ölçüyor. `From` gerçekleştirimi bu indirgemenin nerede yapıldığını gösteriyor: kaynak
hatanın verisi, hedef tip kuruluyorken elde tutulur ya da atılır — bu, gerçekleştirimi
yazan kişinin kararı.

Bu konunun ilk üç dersi, `Option` ve `Result`ın kendi başarı ve başarısızlık kollarının kaç
kez çalıştığını ölçtü; kollardaki hata değerlerinin *kendisi* önemsizdi, `SayiHatasi`
hiçbir bilgi taşımayan boş bir yapıydı. Bu ders o boşluğu dolduruyor: `SayiHatasi` artık
bir `alan` taşıyor ve bu alanın doğru mu yanlış mı dolduğu, dönüşümün nerede ve nasıl
yapıldığına bağlı hâle geliyor.

Ders başlığındaki "özel" sözcüğü, bir hata tipinin kaynağa özgü olarak tasarlandığını
işaret ediyor: `SayiHatasi` standart kütüphanenin bir parçası değil, bu programın kendi
başarısızlık biçimini taşımak için kurulmuş bir yapı. Gerçek bir programda böyle bir tip
tek bir alan yerine bir numaralandırma da olabilir — her varyant farklı bir başarısızlık
türünü taşıyabilir (bir alanın hiç bulunmaması, bir sayının aralık dışı olması, bir
biçimin uyuşmaması gibi). Bu ders bu çeşitliliği kurmuyor; tek bir yapı ve tek bir alanla,
dönüşümün kim tarafından çağrıldığı sorusuna odaklanıyor. Çeşitlenmiş bir hata tipinin her
varyantı da aynı `From` mekaniğini kullanabilir; ölçülen eksen değişmez.

#### Kim Çağırıyor: Örtük Dönüşüm

Bir hata tipi, başka bir hata tipinden `From` gerçekleştirimiyle üretilebiliyorsa, soru
işareti bu üretimi otomatik olarak çağırıyor. Çağıran tarafın kaynağında bu çağrıyı
gösteren hiçbir satır yok — `?` yalnız oradaki tek karakter.

Aşağıdaki koşum, üç alanlı bir kaydı ayrıştıran küçük bir zinciri temsil ediyor: `yas`,
`puan`, `deneme` alanlarının her biri `halka` işleviyle sırayla denetleniyor. Zincirin
kendisi doğrusal — bir alan diğerini beklemiyor, hepsi bağımsız çağrılıyor — ama "zincir"
sözü burada üç ayrı halkanın toplamını anlatıyor: kaç halkada dönüşümün gerçekten
çalıştığı, üçünün toplamından okunuyor.

```rust
// donusum.rs — kere.rs'in SayiHatasi/From kesiti: kac halkada donusum kostu
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

fn say(defter: &Defter, ad: &str) {
    *defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}
fn kere(defter: &Defter, ad: &str) -> u32 {
    *defter.borrow().get(ad).unwrap_or(&0)
}

struct SayiHatasi {
    alan: String,
}

impl From<std::num::ParseIntError> for SayiHatasi {
    fn from(_: std::num::ParseIntError) -> SayiHatasi {
        SayiHatasi { alan: String::from("bilinmiyor") }
    }
}

fn halka(_alan: &str, metin: &str) -> Result<i32, SayiHatasi> {
    let sayi: i32 = metin.parse()?;
    Ok(sayi)
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let kayit = [("yas", "otuz"), ("puan", "78"), ("deneme", "uc")];

    for (alan, deger) in kayit {
        match halka(alan, deger) {
            Ok(sayi) => println!("{:<8} basarili: {}", alan, sayi),
            Err(hata) => {
                say(&defter, "hata-donusumu");
                println!("{:<8} basarisiz, kayitli alan: {}", alan, hata.alan);
            }
        }
    }
    println!();
    println!("zincirdeki halka sayisi: {}", kayit.len());
    println!("donusum kosan halka: {} kez", kere(&defter, "hata-donusumu"));
}
```

```
yas      basarisiz, kayitli alan: bilinmiyor
puan     basarili: 78
deneme   basarisiz, kayitli alan: bilinmiyor

zincirdeki halka sayisi: 3
donusum kosan halka: 2 kez
```

**HT10.** `halka`nın gövdesinde `metin.parse()?` dışında hiçbir satır `SayiHatasi`den söz
etmiyor — `parse`in ürettiği `ParseIntError`, `halka`nın dönüş tipi olan `SayiHatasi`e
otomatik dönüşüyor, çünkü `SayiHatasi` için bir `impl From<ParseIntError>` yazılı.
`kayit`teki üç halkadan ikisi (`"yas"` ve `"deneme"`) ayrıştırılamıyor ve `hata-donusumu`
iki kez artıyor; `"puan"` ayrıştırılabildiği için o halkada dönüşüm hiç çalışmıyor.
Zincirdeki halka sayısı sabit (üç) kalırken, dönüşümün kaç halkada gerçekten koştuğu
girdiye bağlı — burada üçte iki.

**HT11.** Yazdırılan `alan` değerlerinin ikisi de `"bilinmiyor"` — gerçekte başarısız olan
alanlar `"yas"` ve `"deneme"` olsa da, hata değerinin kendisi bunu bilmiyor. `From`
gerçekleştiriminin imzası sabit: yalnız `ParseIntError`i alıyor, `halka`nın hangi `alan`
argümanıyla çağrıldığını bilmiyor. Dönüşüm gerçekten çalışıyor — `hata-donusumu` sayacı bunu
kanıtlıyor — ama çağıran taraf (`halka` içindeki `?`) bu dönüşüme hiçbir bağlam
geçiremiyor, çünkü dönüşümü çağıran, `halka`nın kendisi değil, derleyicinin `?` için
ürettiği koddur.

`halka` işlevinin gövdesinde `SayiHatasi`den söz eden tek yer dönüş tipi (`Result<i32,
SayiHatasi>`); gövdenin kendisi yalnız `metin.parse()?` yazıyor. Bu, kısa yazımın önceki
derste ölçülen faydasının aynısı — kaynak kısa kalıyor — ama burada bedeli de görünüyor:
kaynak kısaldıkça, o kaynağın *çağırabileceği* bilgi de kısıtlanıyor. `halka`nın çağıranı
`alan`ı biliyor (`kayit`teki ilk eleman), ama `halka`nın kendi gövdesi bu bilgiyi
`SayiHatasi`e aktaracak bir yol taşımıyor, çünkü aktarım `?`nin arkasında, `halka`nın
kontrolü dışında gerçekleşiyor.

`hata-donusumu` sayacının ikisi de "her seferinde" sınıfına giriyor: girdi geçersiz her
alan için, dönüşüm kaçınılmaz olarak koşuyor — koşullu değil, ihtimale bağlı değil, girdi
geçersizse dönüşüm de kesinlikle çalışıyor. Bu, önceki dersteki `tembel-varsayilan-gerekli`
ya da `soru-sonrasi-basarili` gibi "yalnız gerektiğinde" sınıfından farklı: oradaki sayı
girdiye bağlı olarak sıfır ya da bir olabilirdi, burada her başarısız halka için dönüşüm
kesin olarak bir kez koşuyor, koşulsuz.

`kayit` dizisindeki üç eleman, koşumun her tekrarında aynı sırayla işleniyor; bu sırayı
değiştirmek `hata-donusumu` sayısını değiştirmiyor, çünkü her halka bağımsız değerlendiriliyor
— önceki bir halkanın başarılı ya da başarısız olması, sonraki halkanın sonucunu
etkilemiyor. Bu, önceki derste bir doğrulama işlem hattı olarak tanıtılan aynı düzenin
devamı: burada her halkanın ek olarak bir dönüşüm çalıştırıp çalıştırmadığı da sayılıyor.
Elde edilen "2 kez" sayısı, üç halkadan tam olarak kaçının başarısız olduğunu ve o
başarısızlığın her birinde dönüşümün gerçekten tetiklendiğini birlikte söylüyor.

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

Her hata dönüşümü bilgi kaybedebilir. Dönüşen tip, kaynağı taşımıyorsa, kaybedilen bilgi
elle taşınmadıkça geri getirilemez. Bu, `SayiHatasi`nin özel bir eksikliği değil, `From`
imzasının doğal bir sonucu: `fn from(deger: Kaynak) -> Hedef` yalnız `Kaynak` tipindeki
değeri alıyor, çağrıldığı yerin çevresindeki hiçbir değişkeni görmüyor. Bu sınır her
`From` gerçekleştirimi için aynı — yalnız `SayiHatasi` gibi kaynak hatanın *dışında* bir
bilgiye (`alan` adı gibi) ihtiyaç duyan tipler için görünür hâle geliyor. Aşağıdaki koşum
aynı senaryoyu, dönüşümü elle yazarak tekrarlıyor.

```rust
// elle_donusum.rs — donusumu cagiran taraf acikca yaziyor, baglami kaybetmiyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

fn say(defter: &Defter, ad: &str) {
    *defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}
fn kere(defter: &Defter, ad: &str) -> u32 {
    *defter.borrow().get(ad).unwrap_or(&0)
}

struct SayiHatasi {
    alan: String,
}

fn halka_elle(alan: &str, metin: &str) -> Result<i32, SayiHatasi> {
    metin
        .parse::<i32>()
        .map_err(|_| SayiHatasi { alan: alan.to_string() })
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let kayit = [("yas", "otuz"), ("puan", "78"), ("deneme", "uc")];

    for (alan, deger) in kayit {
        match halka_elle(alan, deger) {
            Ok(sayi) => println!("{:<8} basarili: {}", alan, sayi),
            Err(hata) => {
                say(&defter, "hata-donusumu");
                println!("{:<8} basarisiz, kayitli alan: {}", alan, hata.alan);
            }
        }
    }
    println!();
    println!("zincirdeki halka sayisi: {}", kayit.len());
    println!("donusum kosan halka: {} kez", kere(&defter, "hata-donusumu"));
}
```

```
yas      basarisiz, kayitli alan: yas
puan     basarili: 78
deneme   basarisiz, kayitli alan: deneme

zincirdeki halka sayisi: 3
donusum kosan halka: 2 kez
```

**HT12.** Sayılar birebir aynı: üç halkadan iki tanesinde dönüşüm koşuyor. Ama `alan`
değerleri artık doğru — `"yas"` başarısız olduğunda hata `"yas"` yazıyor, `"deneme"`
başarısız olduğunda hata `"deneme"` yazıyor. Fark, `map_err`e verilen kapanışın `alan`
parametresini *yakalayabilmesi*: `halka_elle`nin kendi kapsamındaki `alan` değişkeni,
kapanışın içinden erişilebilir durumda. `From::from` böyle bir yakalamaya izin vermiyor —
imzası yalnız kaynak hatayı alıyor, çağıran bağlamdan hiçbir şey almıyor. Bu, kısa yazımın
(`?`) neden bazen yetersiz kaldığının somut kanıtı: kısa yazım hızlı ve kısa, ama yalnız
hedef tipin kaynak hatadan üretebileceği kadar bilgi taşıyabiliyor; kaynak hatanın
*dışındaki* bir bağlamı (burada `alan` adı) taşıması gerekiyorsa, elle yazılan bir dönüşüm
gerekiyor.

Bu sınır genel bir kurala işaret ediyor: bir dönüşüm, hedef tip kaynağı ya da onun
bağlamını tutmuyorsa geri dönüşsüzdür. `SayiHatasi`nin `alan` alanı olması bu kaybı
görünür kılıyor — alan boş bir yapı olsaydı (önceki iki derste olduğu gibi), kaybedilecek
bir şey de olmazdı, çünkü taşınacak bilgi zaten yoktu. Bilgi taşıyan bir hata tipi
tasarlandığında, bu tasarım kararı da beraberinde geliyor: örtük dönüşüm mü, yoksa elle
yazılan ve bağlamı koruyan bir dönüşüm mü?

`halka` ile `halka_elle` arasındaki fark, önceki iki derste görülen "aynı sayı, farklı
kaynak uzunluğu" örneğinin bir varyasyonu. Orada kısa yazım ile elle yazım aynı sayıyı
veriyordu ve fark yalnız satır sayısındaydı; burada da sayı aynı kalıyor, ama fark artık
satır sayısında değil, hatanın *taşıdığı bilgide*. Kısa yazımın kazandırdığı kısalık, bu
kez bir bedelle geliyor — ve o bedel, koşum sayacıyla değil, yazdırılan `alan` değeriyle
görünür oluyor.

Bu iki tasarım arasında seçim yaparken sayı tek başına yeterli bir ölçüt değil — iki koşum
da aynı "donusum kosan halka: 2 kez" satırını verdi. Karar, kaynağın *ne kadar kısa*
kalacağı ile hatanın *ne kadar bilgi* taşıyacağı arasında. `halka` bir satırlık gövdesiyle
en kısa yazımı temsil ediyor, ama hatası her zaman `"bilinmiyor"` diyor. `halka_elle` üç
satırlık bir `map_err` çağrısı taşıyor, ama hatası doğru alanı söylüyor. Bir kod tabanında
her iki yazım da bulunabilir: hatanın hangi alandan geldiği önemli değilse (yalnız "bir
şey başarısız oldu" bilgisi yeterliyse) kısa yazım tercih edilebilir; hatanın kaynağı
kullanıcıya ya da bir günlük kaydına yansıyacaksa, elle yazılan dönüşüm gerekir.

Aynı örtük/elle ayrımı, dönüşümün *maliyeti* açısından da bir soru doğuruyor — ama bu ders
o maliyeti ölçmüyor. `From::from`ün gövdesi burada tek bir yapı kurmaktan ibaret;
`map_err`in kapanışı da aynı işi yapıyor. İkisinin de kaç kez çalıştığı yukarıda ölçüldü
(iki), ikisinin de gövdesi aynı büyüklükte. Fark yalnız *neyin* kurulduğunda: biri sabit
bir dizge kuruyor, öbürü çağıran bağlamdan gelen bir dizgeyi.

Trait Tanımı ve Gerçekleştirimi dersi, bir trait'in varsayılan gövdesinin bir
tipte hiç çalışmayabileceğini, başka bir tipte çalışabileceğini ölçmüştü — aynı sözleşmenin
iki farklı gerçekleştirimi, iki farklı koşum sayısı verebiliyordu. `From<ParseIntError>
for SayiHatasi` da bir trait gerçekleştirimi ve bu ders aynı ilkeyi taşıyor: gerçekleştirim
kaynakta tek bir kez yazılır, ama koşumda girdiye bağlı olarak sıfır ya da daha çok kez
çağrılabilir. Farkı, bu gerçekleştirimin çağrısının kaynakta hiçbir zaman açıkça
görünmemesi — `?`nin arkasında kalması.

#### Özet

- Bir hata tipi başka bir hata tipinden `From` ile üretilebiliyorsa, soru işareti bu
  üretimi otomatik çağırır; çağıran tarafın kaynağında bu çağrıyı gösteren bir satır
  bulunmaz.
- Bir zincirdeki halkaların hangilerinde dönüşümün gerçekten koştuğu sayılabilir; burada
  üç halkadan ikisinde dönüşüm **her seferinde** koşuyor — her başarısız halka için
  kaçınılmaz, koşullu değil.
- `From::from`ün imzası yalnız kaynak hatayı alır; çağıran bağlamdan (örneğin hangi alanın
  başarısız olduğu) hiçbir bilgi alamaz.
- Elle yazılan bir dönüşüm (`map_err` ile bir kapanış), çağıran kapsamdaki değişkenleri
  yakalayabildiği için bu bilgiyi koruyabilir — aynı sayıyı verirken daha fazla bilgi
  taşır.
- Her hata dönüşümü bilgi kaybedebilir; hedef tip kaynağı ya da bağlamı taşımıyorsa, bu
  kayıp geri döndürülemez.
- Seçim, kaynağın kısalığı ile hatanın taşıdığı bilginin zenginliği arasındadır; hangisi
  seçilirse seçilsin, dönüşümün kaç kez koştuğu sayı olarak aynı kalır.

#### Sonraki Adım

Bu konunun ilk dört dersi, bir işlemin başarısız *olabileceği* durumları ölçtü — `Option`ın
yokluğu, `Result`ın kolları, kısa yazımın sessiz atlaması, dönüşümün bilgi kaybı. Hepsinde
ortak olan bir şey var: çağıran taraf başarısızlığı bir değer olarak aldı ve ne yapacağına
kendisi karar verdi — devam etti, bir yedek kullandı, erken döndü, bilgiyi taşıdı ya da
kaybetti. Ama karar her zaman çağıranındı.

Panik ve Kurtarılamaz Hatalar dersi, bunun tam tersini ölçüyor: bir başarısızlığın hiçbir
değer üretmeden, çağırana hiçbir seçim bırakmadan, kalan gövdeyi olduğu gibi durdurduğu bir
tasarım. `Result` dönen bir işlev, başarısızlığını bir veri olarak sunar ve çağıranın onu
görmezden gelmesine bile izin verir; bu konunun son dersinde ölçülen tasarım, tam tersine,
görmezden gelinemeyen bir durdurma modelliyor — ve bu iki tasarımın aynı başarısızlık için
verdiği kalan-gövde sayılarının nasıl ayrıştığını karşılaştırıyor.

### Panik ve Kurtarılamaz Hatalar

Source: https://academia.sh/tr/kurslar/rust-soyutlama/panik-ve-kurtarilamaz-hatalar

Bir başarısızlığın çağırana hiçbir seçim bırakmadan kalan gövdeyi durdurduğu bir tasarımın, aynı başarısızlığın bir dönüş değeriyle karşılandığı tasarımla yan yana modellenmesi ve iki tasarımın kalan gövde için verdiği sayıların karşılaştırılması.

Go Temelleri kursu (M09/K01), bir panik durumunu gerçekten tetikleyip bir kurtarma
çağrısıyla yakalayarak `panik=true` / `panik=false` biçiminde bir sonuç bastı — panikte
yığıt izi, kurtarmanın kendisinde ise yalnız bir bayrak görünür kılındı. O kurs bu yakalama
düzeneğini kullanabildi, çünkü panik orada tek bir çağrının sınırında, denetimli biçimde
tetiklendi. Bu ders aynı ayrımı hiç tetiklemeden ölçüyor: gerçek bir panik durumu ne
kurulur ne de bir kurtarma çağrısıyla yakalanır. Panik metni ortama bağlı bir veridir —
dosya adı, satır numarası, çağrı yığıtının biçimi çalıştırılan makineye göre değişir — ve
bu ders böyle bir metin basmaz.

Bunun yerine ayrım **modellenir**: aynı başarısızlık bir kez `Result` ile karşılanan bir
tasarımda, bir kez de "buradan sonrası koşmaz" biçiminde yazılan bir tasarımda kuruluyor
ve iki tasarımın kalan gövde için verdiği sayılar karşılaştırılıyor. Özel Hata Tipleri
dersi, bir başarısızlığın bir değer olarak taşınabileceğini ve çağıranın bu değerle ne
yapacağına kendisi karar verdiğini gösterdi. Bu ders o kararın *reddedildiği* bir tasarımı
ölçüyor: çağırana hiçbir seçim bırakmayan bir başarısızlık.

Bu konunun önceki dört dersinde ölçülen her düzenek — yokluk, başarısızlık kolları, kısa
yazım, dönüşüm — bir ortak varsayımı paylaşıyordu: başarısızlık bir *değerdi* ve program
o değerle birlikte çalışmaya devam ediyordu. Bu son ders o varsayımın sınırını arıyor: her
başarısızlık bu biçimde ele alınabilir mi, yoksa bazı durumlar için "devam et" seçeneğinin
kendisi anlamsız mı? Kurtarılamaz bir durum, tanımı gereği, `Result` döndürüp çağırana
bırakılamayacak kadar temel bir varsayımın çöktüğü durumdur — ve bu ders bu iki türü
birbirinden ayırmak yerine, ikisinin *kalan gövde* üzerindeki etkisini sayıyor.

#### İki Tasarım, Aynı Koşul

Ders başlığındaki iki sözcük — panik ve kurtarılamaz hata — aynı fikrin iki adı. "Panik",
Rust'ın çalışma zamanının bu durumu adlandırma biçimi; "kurtarılamaz hata", aynı durumun
kavramsal adı, yalnız Rust'a özgü değil. Bu ders ikisini de aynı ölçünün altında ele
alıyor: bir başarısızlığın, çağırana devam etme seçeneği bırakmadan, kalan işi durdurması.

Aşağıdaki iki koşum aynı geçersiz girdiyi (`-5`) iki farklı tasarımla ele alıyor. İlkinde
başarısızlık bir `Result` ile karşılanıyor ve çağıran, hatayı gördükten sonra da kalan
işini sürdürüyor.

```rust
// kurtarilabilir.rs — hata bir deger, cagiran devam etmeyi seciyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

fn say(defter: &Defter, ad: &str) {
    *defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}
fn kere(defter: &Defter, ad: &str) -> u32 {
    *defter.borrow().get(ad).unwrap_or(&0)
}

fn dogrula(giris: i32) -> Result<i32, ()> {
    if giris < 0 {
        return Err(());
    }
    Ok(giris)
}

fn kalan_is(defter: &Defter) {
    say(defter, "kurtarilabilir-devam");
}

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

    match dogrula(-5) {
        Ok(deger) => println!("gecerli deger: {}", deger),
        Err(()) => println!("gecersiz giris karsilandi, program suruyor"),
    }
    kalan_is(&defter);

    println!();
    println!("kurtarilabilir-devam: {} kez", kere(&defter, "kurtarilabilir-devam"));
}
```

```
gecersiz giris karsilandi, program suruyor

kurtarilabilir-devam: 1 kez
```

**HT13.** `dogrula(-5)` bir `Err` döndürüyor, ama bu dönüş programı durdurmuyor: `match`
`Err` kolunu işletiyor, sonra akış olağan biçimde `kalan_is`e ulaşıyor ve onu çalıştırıyor.
`kurtarilabilir-devam` bire çıkıyor. Çağıranın elinde bir seçenek vardı — hatayı görüp
devam etmek — ve bu seçeneği kullandı.

Bu tasarımda `dogrula` başarısızlığını, tam olarak bu konunun önceki dört dersinde ölçülen
düzenekle bildiriyor: `Result<i32, ()>` dönüyor, `Err` kolu bir değer taşımıyor (`()`),
ama varlığı yeterli. `main`in gövdesi bu değeri `match`le karşılıyor ve iki kolda da
program aynı yere, `kalan_is`e ulaşıyor. `kalan_is`in çağrılması `match`in bir sonucu değil
— `match` bittikten *sonra*, sıradan bir sonraki satır olarak yazılmış. Bu, önceki
derslerde görülen kalıptan bir fark: orada kollar birbirinden bağımsız işler yapıyordu, burada
her iki kol da aynı sonraki adıma çıkıyor, yalnız farklı bir mesaj yazdırarak.

İkinci tasarım aynı koşulu, çağırana bu seçeneği hiç sunmadan ele alıyor.

```rust
// kurtarilamaz.rs — kosul saglanmayinca kalan govde tasarim geregi hic cagrilmiyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

fn say(defter: &Defter, ad: &str) {
    *defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}
fn kere(defter: &Defter, ad: &str) -> u32 {
    *defter.borrow().get(ad).unwrap_or(&0)
}

fn devam_isi(defter: &Defter) {
    say(defter, "kurtarilamaz-devam");
}

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

    if giris >= 0 {
        println!("gecerli deger: {}", giris);
        devam_isi(&defter);
    } else {
        println!("kurtarilamaz kosul: kalan govde tasarim geregi hic cagrilmiyor");
    }

    println!("kurtarilamaz-devam: {} kez", kere(&defter, "kurtarilamaz-devam"));
}
```

```
kurtarilamaz kosul: kalan govde tasarim geregi hic cagrilmiyor
kurtarilamaz-devam: 0 kez
```

**HT14.** Aynı koşul (`giris < 0`), bu kez `devam_isi`nin hiç çağrılmamasıyla sonuçlanıyor.
`kurtarilamaz-devam` sıfırda kalıyor — kaynakta yazılı, ama bu koşumda **hiç** çalışmıyor.
İki koşum yan yana konduğunda fark netleşiyor: aynı geçersiz girdi, bir tasarımda kalan
işin çalışmasına, öbüründe hiç çalışmamasına yol açıyor. Sayı, tasarımın kendisinden
kaynaklanıyor — girdinin "ne kadar geçersiz" olduğundan değil.

Bu ikinci tasarımda hiçbir `Result` yok — `main`in kendisi `if`/`else` ile karar veriyor
ve `devam_isi`yi yalnız geçerli dalda çağırıyor. `else` kolunun kendisi bir mesaj
yazdırıyor, ama bu mesaj bir hata *değeri* değil, yalnız bir bilgi notu; programın kendisi
bu mesajdan sonra normal biçimde sona eriyor. Bu ders bu ikinci tasarımı gerçek bir
kurtarılamaz durumun *modeli* olarak sunuyor — gerçek bir kurtarılamaz durum tipik olarak
programı `main`in bile bitmesini beklemeden durdurur; burada `main` olağan biçimde
tamamlanıyor, yalnız `devam_isi` çağrısı atlanmış oluyor. Model, "kalan işin çalışmaması"
sonucunu taşıyor; programın kendisinin ne zaman ve nasıl sonlandığını taşımıyor.

Bu iki program gerçek bir panik durumunu göstermiyor; ikisi de olağan `if`/`match`
dallanmasıyla yazıldı ve ikisi de normal biçimde sonlanıyor. Model, panik durumunun
*gerçek düzeneğini* (çağrı yığıtının geri sarılması) değil, panik durumunun *gözlenebilir
sonucunu* (kalan gövdenin hiç çalışmaması) taşıyor. Gerçek bir panik, bu "hiç çalışmama"
durumunu tek bir çağrıda, ara katmanlardaki her işlevi tek tek durdurmadan, otomatik
olarak üretir — bu ders bu otomasyonu kurmuyor, yalnız sonucunu iki elle yazılmış tasarımla
karşılaştırıyor.

Bu modellemenin neden gerekli olduğu, ortama bağlı veri yasağından geliyor. Gerçek bir
panik durumu tetiklendiğinde, çalışma zamanı bir metin üretir; bu metin hangi dosyada,
hangi satırda durduğunu ve çağrı yığıtının o andaki biçimini içerir. Bu bilgilerin hepsi
derleme ortamına, dosya yoluna ve hatta derleyicinin sürümüne bağlıdır — aynı kaynak kodu
farklı bir makinede derlenip çalıştırıldığında aynı metni üretmez. Bu kurs boyunca
korunan kural (gerçek yol, gerçek bellek adresi, sürüme bağlı çıktı basılmaz) burada da
geçerli; bu yüzden panik durumu gerçekten tetiklenmiyor, yerine yalnız *sayılabilir* olan
kısmı — kalan gövdenin çalışıp çalışmadığı — modelleniyor.

Bu modelleme, önceki dört derste ölçülen "derlenmeyen kaynak kod bloğuna konmaz" kuralıyla
aynı aileden. Orada derleyicinin reddi düzyazıda tarif ediliyor, kod bloğuna konmuyordu;
burada panik durumunun kendisi hiç kurulmuyor, yerine sonucu modelleniyor. İkisinde de
ortak olan, ortama bağlı bir çıktının yerine, kaynaktan okunabilen bir sayının konması.

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

Yukarıdaki iki tasarım arasındaki fark, hangisinin "doğru" olduğunu söylemiyor. Panik bir
hata türü değil, bir tasarım kararıdır — aynı başarısızlık her iki biçimde de yazılabilir
ve seçim çağırana ne bırakılacağına bağlıdır.

**HT15.** Kurtarılabilir tasarım, çağırana bir seçenek bırakıyor: hatayı görmezden gelmek,
bir yedek kullanmak, ya da kendi hatasını üretmek. Kurtarılamaz olarak modellenen tasarım
ise bu seçeneği baştan kapatıyor — kalan iş, koşul sağlanmadıkça tasarım gereği hiç
çalışmıyor ve bunu değiştirmek çağıranın elinde değil. Gerçek bir panik bu ikinci
tasarımı bir adım öteye taşır: durdurma yalnız bir işlevin gövdesinde değil, o işlevi
çağıran her katmanda, hiçbir açık kod yazılmadan gerçekleşir. Bu ders bu çok katmanlı
durdurmayı koşturmuyor; yalnız tek bir katmanda, "hiç çalışmama" sonucunun nasıl
modellenebileceğini gösteriyor.

Bu fark, Hata Yayılımının Kısa Yazımı dersinde ölçülen sessiz atlamayla karşılaştırılabilir,
ama aynı şey değil. Orada erken dönüş bir `Result` üretiyordu; kalan gövde çalışmıyordu,
ama işlev normal biçimde dönüyordu ve çağıran bu dönüşü bir `match` ile karşılayabiliyordu.
Kurtarılamaz olarak modellenen bu tasarımda da işlev normal biçimde dönüyor — model
gerçek bir panik değil — ama gerçek bir panik bu noktada farklılaşır: normal bir dönüş
üretmez, çağıranın `match`le karşılayabileceği bir değer bırakmaz. Bu, kurtarılabilir ve
kurtarılamaz arasındaki asıl çizgi: biri bir değer bırakıyor, öbürü hiçbir değer
bırakmadan akışı kesiyor.

Kurtarılamaz olarak modellenen tasarımda `devam_isi` tek bir gövde olarak yazıldı, ama
gerçek bir programda kalan iş birden çok adımdan oluşabilir — bir kayıt açma, bir toplam
hesaplama, bir sonucu yazdırma. Bu ders bu adımları tek bir işlevde topladı, ama Hata
Yayılımının Kısa Yazımı dersinde görülen çok adımlı atlama burada da geçerli: `giris >= 0`
koşulu sağlanmadığında, `devam_isi`nin *içindeki* bütün adımlar da birlikte atlanır, tek
tek değil. Kalan işin kaç ayrı adımdan oluştuğu önemli değil — koşul sağlanmadığı sürece
hiçbiri çalışmıyor, hepsi birlikte "hiç" sınıfına giriyor.

Bir tasarımcının bu iki yol arasında seçim yapması gereken an, genellikle "bu durum
gerçekten oluşabilir mi, oluşursa programın geri kalanı güvenilir kalır mı" sorusuna
verilen yanıtla belirleniyor. Bir kullanıcı girdisinin geçersiz olması beklenir bir
durumdur — kurtarılabilir tasarım burada doğal bir seçimdir, çünkü çağıran bu durumu
önceden tahmin edebilir ve bir yanıtı hazır tutabilir. Buna karşılık, bir programın kendi
iç tutarlılığını bozan bir durum — örneğin bir dizinin beklenenden az eleman taşıması, bir
kurulumun eksik kalması — genellikle kurtarılamaz olarak ele alınır, çünkü bu durumda
geri kalan kodun hangi varsayımlarla yazıldığı artık geçerli değildir ve devam etmek yeni,
öngörülemeyen hatalara yol açabilir.

Bu iki uç arasında kesin bir sınır çizmek bu dersin işi değil — sınır, programın kendi
alanına ve tasarımcının kararına bağlı. Bu ders yalnız bu kararın *sonucunu* sayılabilir
kılıyor: hangi tasarım seçilirse seçilsin, kalan gövdenin çalışıp çalışmadığı, kaynağı
okuyarak değil, koşumu ölçerek doğrulanabilir. `kurtarilabilir-devam` ile
`kurtarilamaz-devam` arasındaki `1`/`0` farkı, bu konunun başından beri sürdürülen ölçünün
son örneği: kaynakta yazılan bir gövde, koşumda kaç kez çalışıyor.

Bir tasarımın kurtarılabilir mi kurtarılamaz mı olacağı, başarısızlığın *doğasından*
otomatik olarak çıkmaz — bu bir karardır. Aynı geçersiz girdi, bir programda "hatayı
günlüğe yaz, varsayılan değerle devam et" biçiminde ele alınabilir; başka bir programda,
aynı girdi "bu durum asla oluşmamalıydı, devam etmenin bir anlamı yok" biçiminde ele
alınabilir. Rust'ın kendisi bu kararı dayatmaz; `Result` de olağan işlev dönüşü de aynı
dilin parçasıdır, tercih kaynak kodu yazan tarafa aittir.

Yukarıdaki `dogrula` işlevinin `-5` girdisi, aslında iki tasarımda da "aynı" başarısızlığı
temsil ediyor — ikisinde de kaynak, negatif bir sayının geçersiz sayıldığını söylüyor. Fark
başarısızlığın *tespitinde* değil, tespit edildikten *sonra ne olacağında*. Bu, bu konunun
başından beri süren eksenle aynı yerde duruyor: kaynakta yazılan bir gövdenin koşumda kaç
kez çalıştığı. Yalnız bu kez ölçülen gövde bir yedek değer ya da bir dönüşüm değil, bir
işlevin normal akışının *geri kalanı* — ve bu geri kalanın çalışıp çalışmadığı, doğrudan
tasarımın kendisinden okunuyor.

#### Özet

- Aynı başarısızlık iki tasarımla yazılabilir: kurtarılabilir tasarımda çağıran kalan
  işi kendi seçimiyle sürdürür, kurtarılamaz olarak modellenen tasarımda kalan gövde
  **hiç** çalışmaz.
- Gerçek bir panik durumu bu derste hiç tetiklenmedi; ayrım, panik metni basılmadan,
  olağan dallanmayla modellendi — çünkü panik metni dosya, satır ve çağrı yığıtı gibi
  ortama bağlı veri taşır.
- Kurtarılabilir tasarımda kalan iş çalıştı (`kurtarilabilir-devam: 1`), kurtarılamaz
  olarak modellenen tasarımda çalışmadı (`kurtarilamaz-devam: 0`) — aynı koşul, farklı
  sayı.
- Panik bir hata türü değil bir tasarım kararıdır; seçim, çağırana ne bırakılacağına
  bağlıdır — bir durumun beklenir mi yoksa programın iç tutarlılığını mı bozduğuna göre.
- Gerçek bir panik, "hiç çalışmama" sonucunu tek bir çağrıda, ara katmanları tek tek
  durdurmadan üretir; bu ders bu otomasyonu değil, yalnız gözlenebilir sonucunu ölçtü.
- Kurtarılabilir tasarımda çağıran bir değerle karşılaşır ve karar verir; kurtarılamaz
  tasarımda hiçbir değer bırakılmaz, akış doğrudan kesilir.

#### Sonraki Adım

Bu konunun beş dersi boyunca ölçülen gövdeler hep bir işlevin kendi gövdesinin içindeydi —
bir işlev çağrıldığında ne kadarının çalıştığı: bir yedek değer, bir kol, bir dönüşüm, bir
kalan gövde. Beş ders de aynı üç sınıfa (her seferinde, yalnız gerektiğinde, hiç)
yerleşti, yalnız hangi düzeneğin bu sınıflara girdiği değişti. Kapanışlar dersi aynı soruyu,
bir gövdenin *ne zaman* çağrıldığına taşıyor: bir kapanış tanımlandığı anda mı çalışır,
yoksa yalnız biri onu çağırdığında mı? Bu konuda ölçülen "kaç kez koştu" sorusu orada da
sürüyor, ama artık bir başarısızlık değil, bir tanımın kendisi ölçünün konusu.

## Kapanışlar ve Yineleyiciler

Bir gövdenin çevresini yakalayıp sonraya bırakılması, dolaşmanın tek bir sözleşmeye indirgenmesi, zincirlenen dönüşümlerin tüketilene dek beklemesi, ve veri biçimlerine dönüşümün alan başına koşan gövdelerle yapılması.

### Kapanışlar

Source: https://academia.sh/tr/kurslar/rust-soyutlama/kapanislar

Bir kapanışın gövdesi tanımlandığı satırda değil çağrıldığı anda koşuyor: aynı iş doğrudan çağrı, gecikmeli çağrı ve hiç çağrılmayan kapanış biçiminde yazılıp koşum sayıları karşılaştırılıyor ve taşıma ile yakalamanın kaynaktaki değere ne yaptığı ölçülüyor.

Önceki konu, bir gövdenin kalanının hiç çalışmayabileceğini ölçtü: erken dönüş ya da
kurtarılamaz bir hata, kaynakta yazılı adımları sessizce atlıyordu. O ölçüm, hangi dalın
koştuğuna bakıyordu — gövdenin kendisi her zaman aynı yerde, aynı çağrının içindeydi. Bu
ders aynı "kaç kez koştu" sorusunu daha küçük bir birime indiriyor ve dalı değil saklama
biçimini değiştiriyor: artık bir gövdenin bir kez bile koşup koşmayacağı, hangi denetim
dalına düştüğüne değil, o gövdenin çağrılmadan önce nasıl saklandığına bağlı. Bir kapanış,
çevresinden bir değer yakalayıp bir gövdeyi sonraya bırakabilen bir değerdir; bu tanım
Programlama Temelleri kursunda kuruldu ve burada yeniden yazılmıyor. Bu derste ölçülen,
aynı işin üç farklı saklama biçiminde kaç kez koştuğu, aynı kapanışın birden çok kez
çağrılmasının gövdeyi kaç kez koşturduğu ve yakalamanın kaynaktaki değere ne yaptığıdır.
Üç ölçüm de aynı koşum defterini kullanıyor: bir gövde çalıştığında kendi adını bu deftere
işliyor, çalışmadığında hiçbir iz bırakmıyor. Sayaç bu yüzden bir tahmin değil, koşumun
kendisinden okunan bir kayıttır.

#### Aynı İş, Üç Çağrılma Biçimi

Bir gövde üç biçimde yazılabilir: sıradan bir işlev olarak tanımlanıp hemen çağrılabilir;
bir kapanışa sarılıp bir adın altında saklanıp sonra çağrılabilir; ya da bir kapanışa
sarılıp hiç çağrılmayabilir. Kaynak metni üçünde de neredeyse aynıdır — fark, tanımlandığı
yerle çağrıldığı yer arasındaki mesafede yatar. Sıradan bir işlev çağrısında bu mesafe
sıfırdır: tanım ile çağrı aynı satırdadır. Bir kapanış saklandığında bu mesafe açılır ve
gövde, adı tutulduğu sürece, istenildiği an — ya da hiç — çağrılabilir hale gelir. Bu
mesafenin kendisi bir ölçüm konusu değildir; ölçülen, mesafe ne kadar açılırsa açılsın
gövdenin sonunda çağrılıp çağrılmadığıdır.

Aşağıdaki koşumda `dogrudan_govde` bir kapanış değil, iç içe tanımlanmış sıradan bir
işlevdir; bu ayrım bilinçli tutuldu, çünkü Rust'ta iç içe tanımlanan bir işlev çevresinden
hiçbir şey yakalayamaz — `defter` ona parametre olarak açıkça geçiriliyor. Kapanış
sözdizimi (`||`) ise bir gövdeyi, çevresindeki adlara bağlı bir değere çevirir; sıradan bir
işlevle karşılaştırmanın adil kalması için burada bu fark not düşülüyor. Kıyaslanan şey
"kapanış mı değil mi" değil, gövdenin tanımlandığı yerle çağrıldığı yer arasındaki mesafe.

**YN1.** — "aynı işin doğrudan çağrıda koştuğu, gecikmeli kapanışta çağrılana kadar
koşmadığı ve hiç çağrılmayan kapanışta koşumun sonuna kadar hiç koşmadığı" iddiası,
aşağıdaki koşumla doğrulanıyor.

```rust
// kapanis_govdesi.rs — ayni is uc bicimde: dogrudan cagri, gecikmeli cagri, hic cagrilmayan kapanis
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

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

    // dogrudan govde: bir kapanis degil, tanimlanip hemen cagrilan sirasiyla bir islev
    fn dogrudan_govde(defter: &Defter) {
        say(defter, "dogrudan-govde");
    }
    dogrudan_govde(&defter);

    // gecikmeli govde: bir kapanista saklaniyor, tanimlandigi satirda henuz kosmuyor
    let gecikmeli = || say(&defter, "gecikmeli-govde");
    println!("tanimlandiktan hemen sonra: {}", kere(&defter, "gecikmeli-govde"));
    gecikmeli();
    println!("cagirdiktan sonra: {}", kere(&defter, "gecikmeli-govde"));

    // cagrilmayan kapanis: kursun olcum cekirdegindeki ayni kesit, hic cagrilmiyor
    let _kapanis = || say(&defter, "cagrilmayan-kapanis");

    println!("dogrudan govde: {}", kere(&defter, "dogrudan-govde"));
    println!("cagrilmayan kapanis: {}", kere(&defter, "cagrilmayan-kapanis"));
}
```

```
tanimlandiktan hemen sonra: 0
cagirdiktan sonra: 1
dogrudan govde: 1
cagrilmayan kapanis: 0
```

Gecikmeli gövde tanımlandığı satırda henüz koşmuyor: kapanış tanımlandıktan hemen sonra
okunan sayaç sıfır. Açıkça çağrıldığında sayaç bire çıkıyor — tanımlanma ile çağrılma
arasındaki satırlar, gövdenin koşum sayısını hiç etkilemiyor; araya kaç satır, kaç işlev
çağrısı ya da kaç dallanma girerse girsin, `gecikmeli` çağrılana kadar sayaç sıfırda
kalmaya devam ederdi. `cagrilmayan-kapanis` adlı üçüncü gövde ise kursun ölçüm
çekirdeğinin aynı satırıdır: `_kapanis` adı altında saklanıyor ve programın geri kalanında
hiç çağrılmıyor. Bu satır, kursun bağlayıcı tablosunda **hiç** sınıfına düşüyor —
kaynakta yazılı olmak koşmuş olmak anlamına gelmiyor. Bu ölçü, M08/K04'ün ölçtüğü "adımı
hangi iş parçacığı koşturuyor" sorusuyla karışmaz: burada tek bir iş parçacığı var, soru
yalnızca gövdenin kaç kez koştuğudur.

#### Kapanış Kaç Kez Çağrılırsa Gövde O Kadar Koşar

Bir önceki ölçüm, aynı gövdenin bir kez çağrılıp çağrılmadığına bakıyordu. Ama bir kapanış
tek çağrıyla sınırlı değildir: aynı ad altında saklanan aynı kapanış, art arda birden çok
kez çağrılabilir ve her çağrı gövdeyi yeniden koşturur — bir öncekinin sonucu hatırlanmaz,
saklanmaz, yeniden kullanılmaz. Kapanışı sıradan bir değerden ayıran şey gövdesinin içeriği
değil, gövdenin bir ada bağlanıp istenildiği kadar çağrılabilmesidir. Bu, "hiç" ile "en az
bir kez" arasındaki farktan ayrı bir soru: bir kapanış hiç çağrılmayabileceği gibi, aynı
kapanış birden çok kez de çağrılabilir ve bu ikinci durumda sayaç çağrı sayısını birebir
izler.

**YN2.** — "aynı kapanışın art arda üç kez çağrılmasının gövdeyi bir değil üç kez
koşturduğu" iddiası, aşağıdaki koşumda üç ayrı çağrının sayacı üçe çıkarmasıyla
doğrulanıyor.

```rust
// coklu_cagri.rs — govdesi yakaladigi degeri sadece okuyan bir kapanis defalarca cagrilabiliyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let etiket = String::from("olcum-etiketi");

    let coklu_cagrilan = || {
        say(&defter, "coklu-cagrilan-govde");
        etiket.len()
    };

    let a = coklu_cagrilan();
    let b = coklu_cagrilan();
    let c = coklu_cagrilan();

    println!("uc cagridan sonra uzunluklar: {a}, {b}, {c}");
    println!("govde kac kez kosti: {}", kere(&defter, "coklu-cagrilan-govde"));
}
```

```
uc cagridan sonra uzunluklar: 13, 13, 13
govde kac kez kosti: 3
```

`coklu_cagrilan`, yakaladığı `etiket`i yalnızca okuyor — uzunluğunu döndürüyor, değerin
kendisini gövdesinden dışarı taşımıyor. Bu yüzden aynı kapanış üç kez çağrılabiliyor ve her
çağrı sayacı bir artırıyor: üç çağrı, üç koşum. Eğer bu gövde `etiket`i okumak yerine
değerin sahipliğini dışarı taşısaydı — örneğin `etiket`in kendisini olduğu gibi
döndürseydi — ilk çağrıda değeri elinden kaçırır ve derleyici ikinci çağrıyı reddederdi:
gövde artık elinde tutmadığı bir değeri bir daha veremez. Bu redd kaynak koduna konmuyor,
çünkü derlenmeyen bir kaynak dersin kapsamı dışında kalıyor; ama sonucu şudur — yakaladığı
değeri yalnızca okuyan bir kapanış istenildiği kadar çağrılabilir, yakaladığı değeri
tüketen bir kapanış yalnızca bir kez çağrılabilir. İkisi de aynı taşıma kuralının, çağrı
sayısına yansıyan farklı bir yüzüdür; bir kapanışın kaç kez çağrılabileceği, gövdesinin
yakaladığı değere ne yaptığından okunabilir. Bu üç çağrılabilirlik derecesi — istenildiği
kadar, yalnızca değiştirerek, yalnızca bir kez — Rust'ın standart kitaplığındaki üç kapanış
trait'inin karşılığıdır; bu trait'lerin adları ve aralarındaki sınır bu dersin kapsamı
dışında kalıyor, çünkü bir sözleşmenin sınırını okumak kursun ayrı bir dersinin konusu.
Burada ölçülen yalnızca çağrı sayısının kaynağa yansıyan sonucudur.

#### Yakalama Kaynağı Nasıl Değiştiriyor

Kapanışların koşup koşmadığı sorunun bir yarısı; ne yakaladıkları öbür yarısıdır. Bir
kapanış çevresinden bir değeri iki yoldan alabilir: ödünç alarak (referansla) ya da
taşıyarak. Bu seçim gövdenin kaynak metninde açık değildir — derleyici, gövdenin yakalanan
her ada ne yaptığına bakarak kararı kendisi verir; `move` anahtar sözcüğü bu kararı elle
zorlar. Taşıma, Sahiplik ve Bellek Modeli kursunda kurulan sahiplik kuralının kapanışlara
uygulanmış biçimidir: bir değer bir kapanışa taşındığında, o değerin sahibi artık kapanışın
kendisidir ve değerin ömrü kapanışın ömrüne bağlanır — gövdesi hiç koşmasa bile.

**YN3.** — "taşıma ile yakalanan bir değerin, kapanışın gövdesi hiç koşmasa bile kapanışla
birlikte bırakıldığı" iddiası, aşağıdaki koşumda `yakalanan-deger-birakildi` sayacının
kapanış bırakılana kadar sıfır kalıp bırakılınca bire çıkmasıyla, buna karşılık gövdenin
kendi sayacının koşum boyunca sıfırda kalmasıyla doğrulanıyor.

```rust
// yakalama_omru.rs — tasima ile yakalanan degerin omru kapanisin kendisine baglaniyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

struct Kayit {
    defter: Defter,
}

impl Drop for Kayit {
    fn drop(&mut self) {
        say(&self.defter, "yakalanan-deger-birakildi");
    }
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let kayit = Kayit { defter: Rc::clone(&defter) };

    // kapanis "kayit"i tasiyarak yakaliyor: bu satirdan sonra "kayit" adi kullanilamaz
    let hic_cagrilmayan = move || {
        say(&kayit.defter, "cagrilmayan-kapanis-2");
    };
    println!(
        "kapanis tanimlandiktan sonra, hic cagrilmadan: {}",
        kere(&defter, "yakalanan-deger-birakildi")
    );

    drop(hic_cagrilmayan);
    println!("kapanis birakildiktan sonra: {}", kere(&defter, "yakalanan-deger-birakildi"));
    println!("govdesi hic kosmadi: {}", kere(&defter, "cagrilmayan-kapanis-2"));
}
```

```
kapanis tanimlandiktan sonra, hic cagrilmadan: 0
kapanis birakildiktan sonra: 1
govdesi hic kosmadi: 0
```

`Kayit` değeri `move` anahtar sözcüğüyle kapanışa taşınıyor; bu satırdan sonra `kayit` adı
artık kullanılamaz — değerin sahibi kapanış oldu. Kapanış hiç çağrılmadan bırakıldığında
(bir çağrıyla açıkça bitirildiğinde), taşınan `Kayit` değeri de kapanışla birlikte bitiyor
ve bırakma izi deftere düşüyor. Bu, gövdenin koşum sayacından tümüyle ayrı bir olay:
`cagrilmayan-kapanis-2` sayacı koşum boyunca sıfırda kalırken, yakalanan değerin bırakılma
sayacı bire çıkıyor. Kapanışın tanımlanması ve taşınan değeri elinde tutması, gövdesi hiç
çağrılmamasına rağmen kaynak üzerinde gerçek bir etki bırakıyor — yalnızca bu etki, gövde
koşum sayacının ölçtüğü şey değil. Kapanış açıkça bırakılmasaydı bile, kapsamının sonuna
gelindiğinde aynı bırakma yine gerçekleşirdi; burada açık çağrı yalnızca anı gözlemlenebilir
kılmak için kullanıldı, sonucu değiştirmedi.

Bir referansla yakalama, taşımadan farklı olarak kaynaktaki adı canlı tutar; Sahiplik ve
Bellek Modeli kursunda kurulan ödünç alma kuralları burada da geçerlidir ve bu ders onları
yeniden kurmuyor.

**YN4.** — "referansla yakalayan bir kapanışın, yakaladığı adı kapanış tanımlandıktan
sonra da ve kapanış çağrıldıktan sonra da kullanılabilir bıraktığı" iddiası, aşağıdaki
koşumda `etiket` adının üç ayrı satırda da okunabilmesiyle doğrulanıyor.

```rust
// referansla_yakalama.rs — referansla yakalama kaynaktaki adi canli tutuyor, tasima gibi tasimiyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let etiket = String::from("paylasilan-etiket");

    let referansla_yakalayan = || {
        say(&defter, "referansla-yakalanan-govde");
        etiket.len()
    };

    println!("kapanis tanimlandiktan sonra etiket hala okunabiliyor: {etiket}");
    let uzunluk = referansla_yakalayan();
    println!("cagridan sonra: {}", kere(&defter, "referansla-yakalanan-govde"));
    println!("etiket kapanistan sonra da erisilebilir: {etiket} (uzunluk {uzunluk})");
}
```

```
kapanis tanimlandiktan sonra etiket hala okunabiliyor: paylasilan-etiket
cagridan sonra: 1
etiket kapanistan sonra da erisilebilir: paylasilan-etiket (uzunluk 17)
```

`referansla_yakalayan`, `move` anahtar sözcüğü olmadan tanımlanıyor ve gövdesi `etiket`i
yalnızca okuyor; bu yüzden yakalama bir ödünç almaya indirgeniyor, taşımaya değil. `etiket`
adı hem kapanış tanımlandıktan hemen sonra hem de kapanış çağrıldıktan sonra doğrudan
okunabiliyor — önceki koşumdaki `kayit` adının taşımadan sonra kullanılamaz hale gelmesiyle
tam bir karşıtlık. Aynı yakalama sözdizimi (`|| { ... }`), gövdenin yakaladığı değere ne
yaptığına göre iki farklı sahiplik sonucu üretiyor: biri kaynaktaki adı kapatıyor, öbürü
açık bırakıyor. Bu ders bu iki sonucu yeniden kurmuyor, yalnızca ikisinin de gövde koşum
sayısından bağımsız olduğunu ölçüyor. Hangi yolun seçileceği kaynak metinde `move` yazılıp
yazılmadığından okunur; gövdenin kaç kez koştuğu ise bu seçimden değil, kapanışın kaç kez
çağrıldığından belirlenir — iki soru birbirinden bağımsız eksenlerdir.

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

Koşum defteri yalnız gövde çağrılarını sayıyor; bir kapanışın **tanımlanması** da bir
iştir, ama bu iş deftere hiç girmiyor. Bir kapanış tanımlandığında, yakaladığı değerler
(varsa) o an kopyalanıyor ya da taşınıyor, kapanışın kendisi bir değer olarak bellekte bir
yer kaplıyor — bunların hepsi gerçek işlemler, ama hiçbiri bir sayaç çağrısı içermiyor,
çünkü onları tetikleyen kod bizim yazdığımız gövde değil, derleyicinin ürettiği kapanış
temsilidir. Bu yüzden `cagrilmayan-kapanis`in sayacının sıfır olması, kapanışın var
olmadığı ya da bedelsiz olduğu anlamına gelmez; yalnızca gövdesinin hiç koşmadığı anlamına
gelir. Kapanışın kurulma bedeli, bu dersin ölçtüğü sayının kapsamı dışında kalıyor.

Bu sınır, tek bir kapanış için küçük görünür ama birden çok kapanış art arda kurulduğunda
büyür. Bir kapanışın kendisi de bir değerse, o değer başka bir yapıya konabilir, başka bir
kapanışa parametre olarak geçirilebilir ya da bir zincirin bir halkası olabilir — ve
zincirin her halkası, hiç çalışmasa bile kurulma anında bir yer kaplar. Sıradaki konuda bu
gözlem doğrudan işe yarayacak: birden çok kapanış birbirine bağlanıp tek bir dolaşım
sözleşmesi kurduğunda, sözleşmenin kendisi kurulur ama gövdeleri istenmeden koşmaz. Bu
ders o zincirin ilk halkasını — tek bir kapanışı — ölçtü.

Bu dört koşum bir araya geldiğinde ortaya çıkan tablo şudur: bir gövdenin koşup koşmadığı
saklama biçimine, kaç kez koştuğu çağrı sayısına, hangi değerin ne zaman bittiği yakalama
biçimine bağlıdır — ve bu üç sorunun hiçbiri birbirini belirlemez. Bir kapanış hiç
çağrılmadan da bir değeri elinde tutup bırakabilir; defalarca çağrılıp hiçbir değeri
taşımayabilir; ya da yakaladığı değeri tüketip tek bir çağrıyla sınırlı kalabilir. Kaynak
metne bakarak bu üç sorudan hangisinin hangi yanıtı vereceği görülebilir, ama kesin sayı
yalnızca koşumdan okunur.

#### Özet

- Aynı iş üç biçimde yazılabilir: doğrudan çağrı, gecikmeli çağrılan kapanış, hiç
  çağrılmayan kapanış; kapanışlardan yalnız çağrılanlar koşuyor.
- Kursun ölçüm çekirdeğindeki `cagrilmayan-kapanis` gövdesi bu koşumda **hiç** çalışmadı —
  kaynakta yazılı olmak koşmuş olmak anlamına gelmiyor.
- Aynı kapanış birden çok kez çağrılırsa gövde her çağrıda yeniden koşuyor (üç çağrı üç
  koşum); yakaladığı değeri yalnızca okuyan bir kapanış istenildiği kadar çağrılabilirken,
  değeri gövdesinden dışarı taşıyan bir kapanış yalnızca bir kez çağrılabilir.
- Taşıma ile yakalanan bir değerin ömrü kapanışın kendisine bağlanıyor; gövde hiç koşmasa
  da yakalanan değer kapanışla birlikte bırakılıyor.
- Bir referansla yakalama, taşımadan farklı olarak kaynaktaki adı canlı tutuyor —
  taşımanın aksine değerin sahipliğini değiştirmiyor.
- Kapanışın tanımlanması koşum defterine girmeyen ayrı bir iştir; sayı yalnız gövde
  çağrılarını ölçüyor.

#### Sonraki Adım

Bu derste bir kapanışın gövdesi tek başına saklandı ve çağrılıp çağrılmamasına göre koştu
ya da koşmadı; kaç kez çağrıldığı da doğrudan gövdenin koşum sayısına yansıdı. Sıradaki
ders aynı soruyu tek bir kapanıştan bir zincire taşıyor: birçok kapanış aynı sözleşme
etrafında birleştiğinde, sözleşmenin kendisi — yineleyici — bu zincirin ne zaman
ilerleyeceğine karar veriyor. Kurulan bir zincirin, tek bir çağrıyla bile tetiklenmeden
koşum defterine hiç girmediği, bu dersteki `cagrilmayan-kapanis` örneğinin doğrudan bir
uzantısı olarak ölçülecek.

### Yineleyici Trait'i

Source: https://academia.sh/tr/kurslar/rust-soyutlama/yineleyici-traiti

Bir kaynağın üzerinde dolaşmanın tek bir sözleşmeye — sırada bir sonraki değeri isteyen tek bir çağrıya — indirgendiği ve bu sözleşmenin yalnızca istendiğinde ilerlediği ölçülüyor: kurulan bir zincir hiç tüketilmezse gövdesi hiç koşmuyor.

Önceki ders tek bir kapanışın ne zaman koştuğunu ölçtü: saklanan bir gövde, çağrılmadığı
sürece hiç koşmuyordu. Bu ders aynı soruyu tek bir kapanıştan bir kaynağın tamamına
taşıyor. Bir dizi, bir aralık, bir dizgi — hepsi üzerinde "dolaşmak" farklı bir işlem gibi
görünür, ama Rust bunların hepsini tek bir sözleşmeye indirger: sırada bir sonraki değeri
isteyen tek bir çağrı. Bu ders o sözleşmenin kendisini yazıp, dolaşmanın gerçekte bu tek
çağrının tekrarı olduğunu ve bir kaynağın üzerinde kurulmuş bir zincirin bu çağrı hiç
gelmeden koşum defterine hiç girmediğini ölçüyor. Ölçülen, kaynağın türü ya da içeriği
değil; sözleşmenin yönteminin ne zaman ve kaç kez işletildiğidir. Bu, kapanışlar
konusundaki ölçümün doğal bir uzantısı: orada tek bir gövdenin saklanma biçimi
belirleyiciydi, burada birçok kapanışın birbirine bağlandığı bir sözleşmenin kendisi
belirleyici. Sözleşme değişmeden kalırken, onu kim gerçekleştirdiği — bir dizi, bir
aralık, ya da bu derste elle yazılan `Sayici` — değişebilir; ölçülen sayı bu seçimden
bağımsızdır.

#### Dolaşmanın Tek Bir Sözleşmeye İndirgenmesi

Bir `for` döngüsü, bir dönüşüm zinciri, bir toplama çağrısı — kaynak metinde birbirinden
çok farklı görünen bu yazımların hepsi, altta aynı tek yönteme iniyor: sırada bir sonraki
değeri iste, yoksa dur. Bu yöntemin adı ve imzası tek bir sözleşmede toplanıyor; bir tip bu
sözleşmeyi gerçekleştirdiğinde, üzerinde dolaşılabilir hale geliyor. Sözleşmenin kendisi
ne kaynağın nasıl saklandığını, ne de kaç elemanı olduğunu söylüyor — yalnızca "bir
sonrakini ver" sorusuna nasıl yanıt vereceğini.

Standart kitaplığın kendi kaynakları (bir büyüyen dizi, bir aralık) bu sözleşmeyi zaten
gerçekleştiriyor, ama gerçekleştirimin içine bir sayaç yerleştirilemez — sözleşmenin
yöntemi kaç kez çağrıldı diye doğrudan sorulamaz. Bu yüzden aşağıdaki koşum, sözleşmeyi
kendi yazdığı küçük bir tipte gerçekleştiriyor: `Sayici`, tek bir sayaç tutan ve isteneni
bir bir üreten bir kaynak. Sözleşmenin kendisi standart kitaplığınkiyle birebir aynı;
farklı olan yalnızca, yöntemin her çağrıldığında bunu deftere yazması.

**YN5.** — "üç elemanlı bir kaynak üzerinde dolaşmanın, kaynağın kendi sözleşme
gerçekleştiriminin tek bir yöntemine dört kez uğradığı" iddiası, aşağıdaki koşumla
doğrulanıyor.

```rust
// sayici_dolasim.rs — dolasmanin tek bir sonraki-deger cagrisina indirgendigini olcuyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

struct Sayici {
    su_an: u32,
    sinir: u32,
    defter: Defter,
}

impl Iterator for Sayici {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        say(&self.defter, "sonraki-deger-cagrisi");
        if self.su_an < self.sinir {
            self.su_an += 1;
            Some(self.su_an)
        } else {
            None
        }
    }
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let sayici = Sayici { su_an: 0, sinir: 3, defter: Rc::clone(&defter) };

    let mut toplam = 0;
    for deger in sayici {
        toplam += deger;
    }

    println!("toplam: {}", toplam);
    println!("sonraki deger cagrisi sayisi: {}", kere(&defter, "sonraki-deger-cagrisi"));
}
```

```
toplam: 6
sonraki deger cagrisi sayisi: 4
```

`Sayici`, sözleşmenin tek yöntemini gerçekleştiriyor: bir sonraki değer varsa onu veriyor
ve kendi durumunu bir ilerletiyor, yoksa boş bir yanıt döndürüyor. Kaynak metinde `for`
döngüsü tek bir yazım gibi görünse de, koşumda bu yöntem üç elemanlı `Sayici` üzerinde dört
kez çağrılıyor: üç kez bir değerle, bir kez de "artık yok" yanıtıyla. Döngü bu dördüncü
çağrıyı görene kadar durmuyor — kendisi eleman saymıyor, yalnızca yanıtı boş olana kadar
sormaya devam ediyor. Toplamın altı çıkması (1 + 2 + 3) ile çağrı sayısının dört olması
aynı koşumun iki farklı yüzü: biri elemanların değerini, öbürü sözleşmenin kaç kez
işletildiğini gösteriyor. Bu ölçü, M08/K04'ün ölçtüğü "adımı hangi iş parçacığı
koşturuyor" sorusuyla karışmaz: burada tek bir iş parçacığı var, soru yalnızca sözleşmenin
kaç kez işletildiğidir.

Dördüncü çağrı, kaynak metinde hiçbir yerde açıkça yazılmıyor — `for` döngüsünün kendisi bu
çağrıyı üretiyor, "artık değer yok" yanıtını görene kadar sormaya devam ederek. Bu, dolaşan
tarafın (döngünün) kaynağın kaç eleman taşıdığını önceden bilmesi gerekmediği anlamına
geliyor: `Sayici`nin sınırı üç olarak kuruldu, ama döngü bu sınırı hiç okumadı, yalnızca
yanıtları izledi. Sözleşme, kaynağın büyüklüğünü kaynağın kendisine saklı tutuyor.

Sözleşmenin tek yöntemi, kaynağın tamamının dolaşılmasını da zorunlu kılmıyor — yalnızca
kaç kez istenirse o kadar ilerliyor. Aynı `Sayici`, sınırı beş olacak biçimde kurulup
yalnızca iki kez sorguluyor.

**YN6.** — "beş elemanlık bir kaynağın yalnızca ilk iki değeri istendiğinde, sözleşmenin
yönteminin beş değil iki kez çağrıldığı" iddiası, aşağıdaki koşumla doğrulanıyor.

```rust
// kismi_tuketim.rs — bir yineleyici sonuna kadar tuketilmeden de birakilabiliyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

struct Sayici {
    su_an: u32,
    sinir: u32,
    defter: Defter,
}

impl Iterator for Sayici {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        say(&self.defter, "kismi-sonraki-deger-cagrisi");
        if self.su_an < self.sinir {
            self.su_an += 1;
            Some(self.su_an)
        } else {
            None
        }
    }
}

fn main() {
    let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
    let mut sayici = Sayici { su_an: 0, sinir: 5, defter: Rc::clone(&defter) };

    let ilk = sayici.next();
    let ikinci = sayici.next();

    println!("ilk iki deger: {:?}, {:?}", ilk, ikinci);
    println!("sinir bes iken cagri sayisi: {}", kere(&defter, "kismi-sonraki-deger-cagrisi"));
}
```

```
ilk iki deger: Some(1), Some(2)
sinir bes iken cagri sayisi: 2
```

`sayici` beş değere kadar ilerleyebilecek biçimde kuruldu, ama yalnızca iki kez açıkça
sorgulandı ve kalan üç değere hiç uğranmadı — `sayici` burada kapsamının sonunda sessizce
bırakılıyor. Sözleşmenin yöntemi, kaynağın "bitmesini" değil, "istenmesini" bekliyor:
kaynak beş eleman taşısa da, taşıdığı sayı çağrı sayısını belirlemiyor, yalnızca üst sınırı
belirliyor. Bu gözlem sıradaki dersin eksenini önceden kuruyor: bir kaynağın uzunluğu ile
üzerinde çalışan gövdenin kaç kez koştuğu aynı sayı değildir.

Bu koşumda yöntem elle, iki ayrı satırda çağrıldı; bir önceki koşumda ise aynı yöntemi bir
döngü çağırıyordu. İkisi arasındaki fark yalnızca çağıranın kimliğinde: birinde çağıran
kod bizim yazdığımız `main`, öbüründe çağıran kod `for` döngüsünün kendisi. Sözleşmenin
yöntemi her iki durumda da aynı imzayı taşıyor ve aynı biçimde yanıt veriyor — kimin
çağırdığı, sözleşmenin davranışını değiştirmiyor, yalnızca çağrının kaynak metinde nerede
göründüğünü değiştiriyor. Bu, sözleşmenin "tek" olmasının tam anlamı: dolaşımın hangi
yazımla ifade edildiğinden bağımsız, altta hep aynı yönteme iniyor.

Sözleşmenin bu tekliği, bir dolaşım biçiminin başka bir dolaşım biçimine dönüştürülmesini
de basitleştiriyor: bir dönüşüm zinciri ya da bir arama çağrısı yazıldığında, bunların
hepsi aynı sözleşmenin üstüne kuruluyor, ayrı bir dolaşım kuralı öğrenmeye gerek kalmıyor.
Bu üst yöntemlerin çoğu, kursun açılış dersinde ölçülen türden varsayılan gövdelerdir —
kendileri `next` çağrısını içermez, `next`i tekrar tekrar çağıran hazır bir gövdeyi
devralırlar; orada ölçülen, bir varsayılan gövdenin onu yeniden yazan bir tip için hiç
koşmadığıydı, burada ölçülen ise aynı mekanizmanın dolaşım sözleşmesindeki somut bir
örneği. Ne var ki bu sözleşmenin var olması, dolaşmanın kaynak kodda yazılmasıyla koşumda
gerçekleşmesinin aynı an olduğu anlamına gelmiyor — bu ayrım sıradaki ölçünün konusu.

#### Kurulmuş Ama Hiç Tüketilmemiş Zincir

Bir kaynak üzerinde bir dönüşüm tanımlamak, o dönüşümü çalıştırmakla aynı şey değildir. Bir
önceki ölçüm sözleşmenin yöntemini elle çağırdı; bu ölçüm aynı sözleşmenin üstüne kurulan,
kaynak metinde çok daha sık görülen bir yazımı — bir dönüşüm zinciri — ele alıyor. Bir
dönüşüm zinciri yazmak (`.map(...)` gibi), kaynağı ve bir kapanışı bir araya getirip yeni
bir kaynak üreten bir çağrıdır; ürettiği şey de kendisi sözleşmeyi gerçekleştiren bir
değerdir. Kursun ölçüm çekirdeğindeki bir satır, dört elemanlı bir kaynağı dönüştüren bir
zincir kuruyor ama bu zinciri hiçbir yerde tüketmiyor — sonucu ne bir döngüye veriyor, ne
bir listeye topluyor, ne de bir toplama çağrısına yolluyor. Zincirin bu haliyle koşup
koşmadığı, tembel değerlendirme kavramının bir uygulamasıdır; bu kavram Programlama
Temelleri kursunda kuruldu ve burada yeniden anlatılmıyor, yalnızca kaç kez koştuğu
sayılıyor.

**YN7.** — "aynı dört elemanlı kaynağın bir kez hiç tüketilmeden, bir kez toplama
çağrısıyla tüketilerek kurulduğunda, tüketilmeyen gövdenin sıfır, tüketilenin dört kez
koştuğu" iddiası, aşağıdaki koşumla doğrulanıyor.

```rust
// tuketilmeyen_ve_tuketilen.rs — ayni kaynak bir kez tuketilmeden, bir kez tuketilerek kuruluyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

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

    // tuketilmeyen: kursun olcum cekirdegindeki ayni kesit, hic tuketilmiyor
    let _tembel = vec![1, 2, 3, 4].into_iter().map(|x| {
        say(&defter, "tuketilmeyen-zincir");
        x * 2
    });

    // ayni kaynak, bu kez toplama cagrisiyla tuketiliyor
    let toplam: i32 = vec![1, 2, 3, 4]
        .into_iter()
        .map(|x| {
            say(&defter, "tuketilen-esdeger-zincir");
            x * 2
        })
        .sum();

    println!("tuketilmeyen govde kac kez kosti: {}", kere(&defter, "tuketilmeyen-zincir"));
    println!(
        "tuketilen govde kac kez kosti: {} (toplam {})",
        kere(&defter, "tuketilen-esdeger-zincir"),
        toplam
    );
}
```

```
tuketilmeyen govde kac kez kosti: 0
tuketilen govde kac kez kosti: 4 (toplam 20)
```

İki zincirin kaynak metni aynı biçimde başlıyor: aynı dört elemanlı kaynak, aynı dönüşüm
gövdesi. Fark yalnızca sonda: birincisi bir adın altında saklanıp bırakılıyor, ikincisi bir
toplama çağrısına veriliyor. `tuketilmeyen-zincir` sayacı koşumun sonuna kadar sıfırda
kalıyor — kursun bağlayıcı tablosunda bu satır **hiç** sınıfına düşüyor. Eşdeğer zincir
tüketildiğinde ise dönüşüm gövdesi dört elemanın her biri için bir kez, toplam dört kez
koşuyor. Kurulmuş bir zincirin kaynakta durması, sözleşmenin sırada bir sonraki değeri iste
çağrısının hiç gelmediği anlamına geliyor; çağrı gelmeden gövde koşmuyor.

Bu iki koşumun tek farkı zincirin kaynak metninde değil, zincirin sonunda ne bulunduğunda.
`.sum()` çağrısı, sözleşmenin yöntemini kaynak bitene kadar tekrar tekrar çağıran bir
tüketicidir; `_tembel` adının altına bırakılan zincirde böyle bir tüketici hiç yok. Bir
zincirin dönüşüm gövdesini yazmak, o gövdeyi koşum defterine kaydetmenin yalnızca ön
koşuludur — yeterli koşulu, zincirin ucuna bir tüketici bağlanmasıdır.

Bu fark, kaynak metni okuyarak da fark edilebilir: `_tembel` adı altını çizen alt çizgi,
Rust'ın "bu ad kullanılmıyor" uyarısını susturmak için oradadır — kapanışın önceki derste
görülen `_kapanis` adıyla aynı işaret. Bir zincirin kurulup hiç kullanılmaması, kaynak
metinde de küçük bir iz bırakıyor: adı okunmayan bir bağlama. Tüketilen zincirde ise sonuç
`toplam` adına bağlanıp yazdırma çağrısında okunuyor; ad kullanılmadan kalmıyor.

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

Tüketilmeyen bir zincirin gövde koşum sayacının sıfır olması, o zincirin bedelsiz olduğu
anlamına gelmez. `map` çağrısı, kaynağı ve dönüşüm kapanışını saran bir yapı kuruyor; bu
yapı `_tembel` adı altında bellekte duruyor, tıpkı önceki dersteki hiç çağrılmayan bir
kapanışın kendisi gibi. Kurulan bu yapının kendisi bir değerdir ve bir yer kaplar — ama bu
yer kaplama, koşum defterinin ölçtüğü "gövde kaç kez koştu" sorusunun kapsamına girmez.
Defter yalnızca dönüşüm kapanışının içindeki çağrıyı sayıyor; zincirin kurulma anında
kapladığı yapıyı saymıyor. Bu ders o bedeli ölçmeye kalkmıyor, yalnızca var olduğunu
kaydediyor. Bu, önceki dersin sınırlayıcı ölçümüyle birebir aynı gözlemin, tek bir
kapanıştan bir kapanış zincirine taşınmış hâlidir.

Aynı sınır, `Sayici` örneğindeki kısmi tüketim ölçümü için de geçerli. Sınırı beş olan bir
kaynağın yalnızca iki değeri istenip geri kalan üçünün hiç istenmemesi, o üç değerin
üretilmediği anlamına geliyor — ama kaynağın kendisinin kurulma bedeli, bu sınırın nerede
kesildiğinden bağımsız olarak zaten ödendi. Ölçülen sayı, kurulma anındaki değil, sonraki
istekteki maliyeti yansıtıyor.

Bu iki gözlem birlikte, kaç kez koştu sorusunun sınırlarını çiziyor: sayaç, sözleşmenin
yönteminin ne zaman çağrıldığını kesin olarak söylüyor, ama o yöntemi barındıran yapının
ne zaman kurulduğunu, ne kadar yer kapladığını söylemiyor. Bir zincir kurulur, bir kısmı
istenir, kalanı hiç istenmeden bırakılır — üç ayrı an, üç ayrı soru ve bu dersin ölçtüğü
sayı bunlardan yalnızca birine, çağrı anına yanıt veriyor. Kurulma anını ve yer kaplamayı
ölçmek isteyen bir soru, farklı bir araç ister; bu kurs o aracı yazmıyor, yalnızca hangi
sorunun bu sayının kapsamı dışında kaldığını açıkça belirtiyor.

#### Özet

- Bir kaynak üzerinde dolaşmak, kaynağın kendi sözleşmesinin tek bir yöntemine tekrar tekrar
  uğramaya indirgeniyor; üç elemanlı bir kaynakta bu yöntem dört kez çağrılıyor.
- Sözleşmenin kendisi kaynağın saklanma biçiminden bağımsızdır; yalnızca "bir sonrakini
  ver" sorusuna nasıl yanıt vereceğini tanımlar.
- Kursun ölçüm çekirdeğindeki `tuketilmeyen-zincir` gövdesi bu koşumda **hiç** çalışmadı —
  aynı kaynağın tüketilen eşdeğeri dört kez çalıştı.
- Zincir kurmak ile zincirin ilerlemesi ayrı olaylardır; kurulma anında sözleşmenin
  yöntemine hiç uğranmaz; beş elemanlık bir kaynağın yalnızca iki değeri istenirse
  sözleşmenin yöntemi de yalnızca iki kez çağrılır.
- Tembel değerlendirmenin kendisi bu derste yeniden kurulmadı; yalnızca kaç kez koştuğu
  sayıldı.
- Kurulmuş ama tüketilmeyen bir zincir bedelsiz değildir — bu bedel gövde koşum sayacının
  ölçtüğü şey değildir; sayaç yalnızca çağrı anını, kurulma anını değil ölçer.

#### Sonraki Adım

Bu derste bir zincirin hiç tüketilmemesi ile tüketilmesi arasındaki fark, tek bir dönüşüm
adımı üzerinden ölçüldü: sıfıra karşı dört. Ama "tüketilmek" tek bir anlam taşımıyor —
kısmi tüketim ölçümünde görüldüğü gibi, bir kaynağın ne kadarının istendiği de sözleşmenin
yöntemine kaç kez uğranacağını belirliyor. Bu derste iki uç durum görüldü: hiç istenmemek
(sıfır) ve tamamı istenmek (dört). Sıradaki ders aynı kaynağı, aynı dönüşüm gövdesini alıp
zincirin ucuna farklı tüketiciler koyuyor ve bu kez zincirin uzunluğunun değil, tüketicinin
gövdenin kaç kez koşacağını belirlediğini gösteriyor — bir tüketici kaynağın tamamını
isteyebilir, bir başkası ilk uygun değeri bulunca durabilir; ikisi de aynı dört elemanlı
kaynaktan başlayacak, ama biri uçlardan birine, öbürü ortasında bir yere düşecek bir sayı
üretecek. Bu iki ders bir arada, "tüketmek" sözcüğünün aslında bir uçtan öbürüne uzanan bir
aralığı kapsadığını gösteriyor: hiç, kısmen, tamamen.

### Yineleyici Bağdaştırıcıları

Source: https://academia.sh/tr/kurslar/rust-soyutlama/yineleyici-bagdasticilari

Aynı dört elemanlı kaynaktan başlayan iki zincirin kaynak metni neredeyse aynı, ama biri gövdeyi her elemanda, öbürü yalnız gerektiği kadar koşturuyor: sayıyı belirleyenin zincirin uzunluğu değil ucuna bağlanan tüketici olduğu ve zincirlenen bağdaştırıcıların gövdelerinin eleman başına sırayla koştuğu ölçülüyor.

Önceki ders bir zincirin hiç tüketilmemesi ile tamamen tüketilmesi arasındaki iki uca
baktı: sıfır ve dört. Ama bu iki uç, "tüketilmek"in taşıdığı tüm olasılıkları
tüketmiyordu — bir kaynağın tamamı istenmeden de, hiç istenmeden de tüketilebileceği bir
orta hâl vardı. Bu ders aynı kaynağı, aynı dönüşüm gövdesini alıp zincirin ucuna farklı
tüketiciler bağlıyor ve ortaya çıkan sayının zincirin uzunluğundan değil, tüketicinin ne
kadar istediğinden geldiğini gösteriyor. İkinci ölçüm ise tek bir bağdaştırıcıdan iki
bağdaştırıcıya geçiyor: iki dönüşüm zincirlendiğinde, gövdeler bir sırayla mı, yoksa bir
başka sırayla mı koşuyor — bu sıra kaynak metinde açıkça yazılı değil, yalnızca koşumdan
okunabiliyor. Bu konunun adı "bağdaştırıcı" olan `map` gibi çağrıları anıyor; bu ders o
adın neden uygun olduğunu sayarak gösteriyor — bir bağdaştırıcı, kaynağı değiştirmeden
onu sarıp aynı sözleşmeyi taşıyan yeni bir kaynağa çeviriyor.

#### Aynı Kaynak, Farklı Tüketici, Farklı Sayı

Bir dönüşüm zincirinin kaynak metni, zincirin ucuna hangi tüketicinin bağlandığını
söylemeden önce kaç kez koşacağını söylemez. Bu, önceki iki dersin ölçülerinin doğal bir
birleşimidir: bir kapanışın gövdesi çağrılana kadar koşmuyordu, bir dolaşım sözleşmesi
istenene kadar ilerlemiyordu — bu ders bu iki gözlemi birleştirip, "ne kadar istendiğinin"
zincirin ucundaki tek bir çağrıyla, tüketiciyle belirlendiğini gösteriyor. Aynı dört elemanlı kaynak ve aynı dönüşüm
gövdesiyle kurulan iki zincir, yalnızca uçları farklı olduğu için farklı sayıda koşabilir:
biri kaynağın tamamını isteyen bir toplama çağrısına bağlanır, öbürü ilk uygun değeri
bulunca duran bir arama çağrısına. Zincirin ortasındaki dönüşüm adımı, hangi tüketicinin
kendisini çağıracağını yazarken bilmiyor — bu bilgi yalnızca zincir tamamlanıp
çalıştırıldığında ortaya çıkıyor.

**YN8.** — "aynı dört elemanlı kaynaktan kurulan iki zincirin, ucuna toplama çağrısı
bağlanan gövdenin dört, ucuna ilk uygun değeri arayan çağrı bağlanan gövdenin iki kez
koştuğu" iddiası, aşağıdaki koşumla doğrulanıyor.

```rust
// tuketilen_ve_erken_biten.rs — kursun olcum cekirdegindeki kesit: zincirin kaynagi ayni, sonu farkli
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

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

    // tuketilen zincir: her elemanda
    let toplam: i32 = vec![1, 2, 3, 4]
        .into_iter()
        .map(|x| {
            say(&defter, "tuketilen-zincir");
            x * 2
        })
        .sum();

    // erken biten zincir: yalniz gerektigi kadar
    let bulunan = vec![1, 2, 3, 4]
        .into_iter()
        .map(|x| {
            say(&defter, "erken-biten-zincir");
            x * 2
        })
        .find(|x| *x > 2);

    println!(
        "tuketilen zincir govdesi kac kez kosti: {} (toplam {})",
        kere(&defter, "tuketilen-zincir"),
        toplam
    );
    println!(
        "erken biten zincir govdesi kac kez kosti: {} (bulunan {:?})",
        kere(&defter, "erken-biten-zincir"),
        bulunan
    );
}
```

```
tuketilen zincir govdesi kac kez kosti: 4 (toplam 20)
erken biten zincir govdesi kac kez kosti: 2 (bulunan Some(4))
```

İki zincirin ilk satırı birebir aynı yapıdadır: aynı dört elemanlı kaynak, aynı `x * 2`
dönüşümü. `.sum()` tüm kaynağı istiyor, bu yüzden dönüşüm gövdesi dört elemanın her biri
için bir kez koşuyor — kursun bağlayıcı tablosunda bu satır **her seferinde** sınıfına
düşüyor: tüketici kaynağın tamamını istediği sürece, gövde her elemanda kaçınılmaz olarak
çalışıyor. `.find(|x| *x > 2)` ise ilk uyan değeri arıyor: kaynak sırasıyla 2, 4, 6, 8
üretirken arama ikinci elemanda (`4 > 2`) duruyor, üçüncü ve dördüncü elemana hiç
uğranmıyor. Bu satır **yalnız gerektiğinde** sınıfına düşüyor — iki değil dört elemanın
tamamı üretilebilirdi, ama tüketici ikinci değerden sonra durmayı seçti; gövde yalnızca
tüketicinin sonucu istediği kadar koştu. Kaynağın uzunluğu ikisinde de dörttü; koşan gövde
sayısı ikisinde de farklı çıktı. Bu ölçü, M08/K04'ün ölçtüğü "adımı hangi iş parçacığı
koşturuyor" sorusuyla karışmaz: burada tek bir iş parçacığı var, soru yalnızca gövdenin
kaç kez koştuğudur.

Zincirin kaynak metnindeki tek fark tüketicinin adıdır — `sum` ya da `find`. Dönüşüm
gövdesinin kendisi, hangi tüketicinin onu çağıracağını bilmiyor ve bilmesi gerekmiyor:
sözleşmenin yöntemi ne kadar istenirse o kadar çalışıyor, kalanını hiç görmüyor. Zincire
üçüncü, dördüncü bir dönüşüm adımı eklense bile, bu ölçü değişmezdi — belirleyici olan
adım sayısı değil, tüketicinin kaç kez "bir sonrakini ver" dediği. Aynı dönüşüm gövdesi,
kaynak metninde hiç değişmeden, yalnızca zincirin ucundaki tek sözcük (`sum` yerine
`find`, `find` yerine başka bir arama ya da sayma çağrısı) değiştirilerek dört, iki, bir
ya da önceki derste görülen sıfır dahil sıfır ile dört arasında herhangi bir sayı
üretebilir.

**YN9.** — "aynı dönüşüm gövdesinin, ilk elemanın işlenmiş hâli aranan değere hemen
uyduğunda yalnızca bir kez koştuğu" iddiası, aşağıdaki koşumla doğrulanıyor.

```rust
// uc_tuketici.rs — ayni govde farkli bir tuketiciyle uculu farkli bir sayi verir
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

type Defter = Rc<RefCell<BTreeMap<String, u32>>>;

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

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

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

    let bulundu = vec![1, 2, 3, 4]
        .into_iter()
        .map(|x| {
            say(&defter, "ilk-elemanda-duran-zincir");
            x * 2
        })
        .any(|x| x == 2);

    println!(
        "ilk elemanda duran zincir kac kez kosti: {} (bulundu mu: {})",
        kere(&defter, "ilk-elemanda-duran-zincir"),
        bulundu
    );
}
```

```
ilk elemanda duran zincir kac kez kosti: 1 (bulundu mu: true)
```

`.any(|x| x == 2)`, dönüştürülmüş ilk değerin (`1 * 2 = 2`) aranan değere zaten uyduğunu
görüp ikinci elemana hiç uğramadan duruyor. Aynı dönüşüm gövdesi bu üçüncü tüketiciyle bir
kez koştu — ne `tuketilen-zincir` ölçümündeki dört, ne `erken-biten-zincir` ölçümündeki
iki. Üç koşum bir arada — bir, iki, dört ve önceki dersten sıfır — aynı gövdenin
yalnızca tüketici değiştirilerek dört ayrı sayı üretebildiğini gösteriyor; sayı gövdenin
değil tüketicinin özelliğidir. Aranan değer ilk elemanda değil de son elemanda uysaydı,
`.any` da tıpkı `.find` gibi kaynağın tamamını dolaşırdı — durma noktası, tüketicinin
tanımından değil, aranan koşulun kaynakta nerede karşılandığından da etkileniyor.

#### Zincirlenen Bağdaştırıcılar Eleman Başına Sırayla Koşuyor

İki dönüşüm adımı art arda zincirlendiğinde, akla gelen iki olası sıralama vardır: ya
birinci adım kaynağın tamamını işleyip bir ara sonuç üretir, sonra ikinci adım bu ara
sonucun tamamını işler (bağdaştırıcı başına toplu); ya da her eleman önce birinci adımdan,
hemen ardından ikinci adımdan geçer, sonra bir sonraki elemana geçilir (eleman başına
sırayla). Bu iki olasılık kaynak metinden ayırt edilemez — `.map(a).map(b)` yazımı
ikisiyle de tutarlı görünür; hangisinin gerçekleştiğini yalnızca koşum söyler.

Bir önceki bölümde kurulan sözleşme burada da geçerli: zincirin tek bir "bir sonrakini
ver" yöntemi var ve bu yöntem zincirin en dışındaki adımdan çağrıldığında, çağrı zincirin
içine doğru yayılıyor. En dıştaki adım kendi bir sonraki değerini istediğinde, bunun için
önce kendinden önceki adımdan bir değer istemesi gerekiyor; o adım da kendinden öncekinden
istiyor — zincir boyunca bu istek geriye doğru yayılıp bir kaynak değeriyle geri
dönüyor. Bu yayılma her seferinde tek bir eleman için oluyor, kaynağın tamamı için değil —
bu da ikinci olasılığı, eleman başına sırayı, işaret ediyor.

Her `map` çağrısı, kendi başına, kaynağın sözleşmesini gerçekleştiren yeni bir değer
üretiyor — bir yineleyiciyi sarıp aynı sözleşmeyi taşıyan başka bir yineleyici. İkinci
`map` çağrısı, birinci `map`in ürettiği bu yeni değeri kaynak olarak alıyor; kendisi de
aynı sözleşmeyi taşıyan üçüncü bir değer üretiyor. Zincirin en dışındaki tüketici bir
sonraki değeri istediğinde, aslında bu iç içe geçmiş üç sözleşmenin en dışındakini
çağırıyor; o da kendi kaynağından (ikinci `map`in ürettiği değer) bir sonrakini istiyor,
o da kendi kaynağından. Bu iç içe geçme, aşağıdaki koşumda tutulan kayıtla doğrudan
gözlemlenebiliyor.

**YN10.** — "iki bağdaştırıcı zincirlendiğinde, birinci adımın bir elemanı işlemesinin
hemen ardından ikinci adımın aynı elemanı işlediği, ikinci elemana geçmeden önce
tamamlandığı" iddiası, aşağıdaki koşumda tutulan sıra kaydıyla doğrulanıyor.

```rust
// bagdastirici_sirasi.rs — iki bagdastirici zincirlendiginde eleman basina sirayla kosuyor
use std::cell::RefCell;
use std::rc::Rc;

type Kayit = Rc<RefCell<Vec<String>>>;

fn kaydet(kayit: &Kayit, metin: String) {
    kayit.borrow_mut().push(metin);
}

fn main() {
    let kayit: Kayit = Rc::new(RefCell::new(Vec::new()));
    let kayit_a = Rc::clone(&kayit);
    let kayit_b = Rc::clone(&kayit);

    let sonuc: Vec<i32> = vec![1, 2, 3]
        .into_iter()
        .map(move |x| {
            kaydet(&kayit_a, format!("birinci-asama-{x}"));
            x + 1
        })
        .map(move |x| {
            kaydet(&kayit_b, format!("ikinci-asama-{x}"));
            x * 2
        })
        .collect();

    println!("sonuc: {:?}", sonuc);
    println!("sira: {}", kayit.borrow().join(", "));
}
```

```
sonuc: [4, 6, 8]
sira: birinci-asama-1, ikinci-asama-2, birinci-asama-2, ikinci-asama-3, birinci-asama-3, ikinci-asama-4
```

Kayıt sırası, bağdaştırıcı başına toplu değil eleman başına sırayla koştuğunu açıkça
gösteriyor: `birinci-asama-1` (birinci adımın ilk elemanı işlemesi) ile `ikinci-asama-2`
(ikinci adımın aynı sonucu hemen işlemesi) art arda geliyor, ikinci elemanın işlenmesi
üçüncü satırda başlıyor. Eğer koşum bağdaştırıcı başına toplu olsaydı, kayıt önce üç
`birinci-asama-*` satırı, sonra üç `ikinci-asama-*` satırı olarak gelirdi — koşum bunu
göstermiyor. Her eleman, zincirin başından sonuna kadar tek bir yolculuk yapıyor; bir
sonraki eleman bu yolculuk bitmeden başlamıyor.

Kayıt tutmak için burada `say`/`kere` çifti yerine paylaşımlı bir kayıt kabı (`Kayit`)
kullanıldı, çünkü bu ölçüm sayı değil sıra istiyor — kaç kez koştuğu değil, hangi
sırayla koştuğu önemli. İki dönüşüm kapanışı, aynı kayıt kabını `Rc::clone` ile ayrı ayrı
paylaşıyor; ikisi de aynı kaba yazabiliyor, çünkü kap kendi içinde değiştirilebilirliğini
taşıyor. Bu düzenek, Sahiplik ve Bellek Modeli kursunda kurulan paylaşımlı sahiplik
kurallarının burada da geçerli olduğunu gösteriyor — bu ders o kuralları yeniden kurmuyor,
yalnızca bir kayıt tutmak için kullanıyor.

Zincire üçüncü bir bağdaştırıcı eklense, aynı düzen sürerdi: her eleman üç adımdan da
sırayla geçer, dördüncü elemana geçmeden önce üçüncü adım da tamamlanırdı. Zincirin
uzunluğu sırayı değiştirmiyor, yalnızca her elemanın kaç durak yapacağını belirliyor.

Bu sıralama, ilk bölümde ölçülen erken durma davranışıyla da tutarlı: eğer zincirin
sonuna `.find` gibi erken duran bir tüketici bağlansaydı, üçüncü elemana hiç
ulaşılmadan zincir dururdu — ama ulaşılan her eleman yine de baştan sona, tüm adımlardan
sırayla geçerdi. Erken durma zincirin *kaç eleman* işlediğini sınırlıyor; eleman başına
sıralama ise işlenen *her bir* elemanın zincirde nasıl ilerlediğini belirliyor. İkisi
aynı sözleşmenin, "bir sonrakini ver" çağrısının, farklı iki sonucu.

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

İki bağdaştırıcının aynı elemanı art arda işlemesi, adımların birbirinden habersiz
olduğu anlamına gelmiyor — sıralama gözlemlenebilir olsa da, bu sıralamayı zincirin kaynak
metninden çıkarım yapıp tahmin etmek yerine bir kayıtla göstermek gerekti; kaynak metindeki
iki ardışık `map` çağrısının yazımı bu sırayı doğrudan söylemiyor. İkinci sınır, bu dersin
ilk ölçümünde görülen: iki zincirin dördü de kaynaktan üretilebilirdi, ama biri ikisinde
durdu. Bu durma kararı zincirin bir yerinde yazılı değil — tüketicinin kendi tanımında.
Aynı dönüşüm gövdesi, farklı bir tüketiciyle zincirlenseydi farklı bir sayı üretirdi; bu
ders o sayının zincirin kendisinden değil tüketiciden geldiğini gösterdi, ama her
tüketicinin ne zaman durduğunu genel bir kuralla vermiyor — her tüketici kendi durma
koşulunu taşıyor.

Üçüncü bir sınır, ölçüm aracının kendisiyle ilgili: her iki koşumda da dönüşüm gövdesine
bir kayıt çağrısı eklendi, ki bu çağrı olmasaydı gövdenin kaç kez koştuğu hiç
gözlemlenemezdi. Kayıt çağrısının kendisi ölçülen işin bir parçası değildir — dönüşümün
asıl işi `x * 2`dir, kayıt yalnızca bir gözlem aracıdır. Bu araç eklenince gövdenin kendi
maliyeti artmış olabilir, ama sayının kendisi (dört, iki) kayıt çağrısı olmasaydı da aynı
kalırdı; ölçüm, ölçtüğü şeyi değiştirmeden gözlemliyor.

Son bir sınır, bu konunun üç dersinin tümüne birden uzanır: ölçülen her sayı tek bir
koşumdan geldi. Kaynağın eleman sayısı, dönüşüm gövdesi ve tüketici sabit tutulduğu sürece
bu sayı koşumdan koşuma değişmiyor — kaynak dört elemanlı bir dizi olduğu, `map` içindeki
gövde `x * 2` olduğu ve `.find(|x| *x > 2)` her seferinde aynı sırayla sorguladığı sürece,
gövde her koşumda tam olarak iki kez koşacak. Bu belirlenimcilik, önceki konulardaki
ölçümlerin dayandığı aynı varsayımdır ve burada da bozulmuyor.

#### Özet

- Aynı kaynak ve aynı dönüşüm gövdesiyle kurulan iki zincir, yalnızca uçlarındaki
  tüketiciye göre farklı sayıda koşabilir: biri **her seferinde** (dört elemanın hepsinde),
  öbürü **yalnız gerektiğinde** (ilk uyan bulununca durarak) koştu.
- Zincirin uzunluğu sabit kaldı (dört eleman); koşan gövde sayısı sabit kalmadı (dört, iki,
  bir) — belirleyici olan tüketicidir, zincirin kendisi değil.
- Dönüşüm gövdesi hangi tüketicinin onu çağıracağını bilmez; sözleşmenin yöntemi ne kadar
  istenirse o kadar çalışır.
- İki bağdaştırıcı zincirlendiğinde gövdeler bağdaştırıcı başına toplu değil, eleman
  başına sırayla koşuyor: bir eleman zincirin başından sonuna kadar tek bir yolculuk
  yapıyor, sonra bir sonraki eleman başlıyor.
- Bu sıralama kaynak metinden doğrudan okunmuyor; bir kayıtla gözlemlenmesi gerekiyor.
- Kayıt tutmak için kullanılan paylaşımlı kayıt kabı, Sahiplik ve Bellek Modeli kursunda
  kurulan paylaşımlı sahiplik kurallarının bir uygulamasıdır; bu kurallar burada yeniden
  kurulmadı, yalnızca bir gözlem aracı olarak kullanıldı.

#### Sonraki Adım

Bu konuda bir kapanışın tek başına ne zaman koştuğu, bir dolaşım sözleşmesinin bu koşmayı
nasıl tek bir yönteme indirdiği ve bir zincirin ucundaki tüketicinin bu sayıyı nasıl
belirlediği ölçüldü. Üç ders birlikte, "kaynakta yazdığın bu gövde kaç kez koştu" sorusunu
giderek büyüyen birimlere uyguladı: tek bir kapanıştan, tek bir sözleşmeye, sözleşmeler
zincirine. Kursun kapanış dersi aynı soruyu somut bir veri biçimine taşıyor: bir yapının
alanlarına uygulanan bir dönüşüm gövdesi, yapı serilirken alan başına kaç kez koşuyor.
Orada zincirin elemanları artık bir dizinin girdileri değil, tek bir yapının alanları
olacak — eleman sayısı sabit ve küçük, tüketici tektir (serileştirmenin kendisi) — ama
sayan aynı soru: kaynakta yazılan bu gövde kaç kez koştu. Bu konuda öğrenilen ayrım orada
da geçerli kalacak: bir gövdenin kaynakta yazılı olması onun koştuğu anlamına gelmiyor,
kaç kez koştuğu da hep aynı sabit sayı olmak zorunda değil — belirleyici, gövdeyi kimin ve
ne zaman çağırdığıdır.

### Dizileştirme

Source: https://academia.sh/tr/kurslar/rust-soyutlama/dizilestirme

Bir yapının alanlarına elle yazılmış bir dönüşüm gövdesi uygulanırken bu gövdenin alan başına kaç kez koştuğunun, iç içe bir yapıda bu sayının nasıl çarpımsal büyüdüğünün ve ters yönde — metinden yapıya — bir alanın başarısızlığının sonraki alanların gövdesini hiç çalıştırmadığının ölçülmesi; kursun on beş dersinde ölçülen bütün gövdeler burada tek bir tabloda toplanıyor.

Yineleyici Bağdaştırıcıları dersi, bir zincirin ucundaki tüketicinin kaynaktaki bir dönüşüm
gövdesini kaç kez çağırdığını ölçtü — zincirin uzunluğu sabit kalırken koşan gövde sayısı
tüketiciye göre değişiyordu. Bu kapanış dersi aynı soruyu somut bir veri biçimine taşıyor: bir
yapının alanlarına uygulanan bir dönüşüm gövdesi, yapı serilirken alan başına kaç kez koşuyor.
Burada zincirin elemanları artık bir dizinin girdileri değil, tek bir yapının alanları; tüketici
de tektir — serileştirmenin kendisi. Dönüşüm bu derste elle yazılıyor, üçüncü taraf bir kasa
kullanılmıyor; bir dizileştirme çerçevesi olsaydı bu gövdeyi kendisi üretirdi, ama burada gövde
kaynakta görünür kalıyor ve sayılabiliyor. Kurs boyunca sorulan soru burada da değişmiyor:
kaynakta yazdığın bu gövde kaç kez koştu? Bu, kursun on beşinci ve son dersi; kapanışında on
dört dersin tamamında ölçülen gövdeler tek bir tabloda toplanacak.

#### Alan Başına Koşan Dönüşüm Gövdesi

Bir dizileştirme çerçevesi olsaydı bu dönüşümü kendisi üretir, alan sayısı arttıkça gövdeyi
görünmez biçimde büyütürdü. Bu ders dönüşümü elle yazıyor: her alan, aynı yardımcı işlevden
geçerek bir dizgeye çevriliyor ve bu geçiş her seferinde sayaç defterine bir kayıt düşüyor.
Ölçüm çekirdeğinin bu kesiti yalnız sayma defterini taşıyor; trait, sınır ya da hata tipi bu
derste kullanılmıyor.

```rust
// kere.rs — kesit: sayma defteri.

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

pub type Defter = Rc<RefCell<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)
}
```

Üç alanlı bir `Nokta` yapısı, alanlarını sırayla dizgeye çeviren bir `dizilestir` yöntemi
taşıyor; her alan aynı `alani_yaz` işlevinden geçiyor ve geçiş sayılıyor. `Cizgi`, iki `Nokta`yı
birden taşıyan bir yapı; kendi `dizilestir`i kendi alanına dokunmuyor, taşıdığı iki `Nokta`nın
`dizilestir`ini çağırıyor. `Ucgen`, üç `Cizgi`yi taşıyan bir yapı; aynı örüntü bir kat daha
derine iniyor.

```rust
// alan-govdesi.rs — bir govdenin alan basina kac kez kostugunu olcer, ic ice buyumeyi gosterir.
mod kere;
use kere::{kere, say, Defter};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

fn alani_yaz(defter: &Defter, ad: &str, deger: i32) -> String {
    say(defter, "alan-govdesi");
    format!("{}={}", ad, deger)
}

struct Nokta {
    x: i32,
    y: i32,
    z: i32,
}

impl Nokta {
    fn dizilestir(&self, defter: &Defter) -> String {
        let x = alani_yaz(defter, "x", self.x);
        let y = alani_yaz(defter, "y", self.y);
        let z = alani_yaz(defter, "z", self.z);
        format!("{};{};{}", x, y, z)
    }
}

struct Cizgi {
    bas: Nokta,
    bitis: Nokta,
}

impl Cizgi {
    fn dizilestir(&self, defter: &Defter) -> String {
        format!("{}|{}", self.bas.dizilestir(defter), self.bitis.dizilestir(defter))
    }
}

struct Ucgen {
    kenar1: Cizgi,
    kenar2: Cizgi,
    kenar3: Cizgi,
}

impl Ucgen {
    fn dizilestir(&self, defter: &Defter) -> String {
        format!(
            "{}#{}#{}",
            self.kenar1.dizilestir(defter),
            self.kenar2.dizilestir(defter),
            self.kenar3.dizilestir(defter)
        )
    }
}

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

    let onceki = kere(&defter, "alan-govdesi");
    let nokta = Nokta { x: 1, y: 2, z: 3 };
    let _ = nokta.dizilestir(&defter);
    let sonraki = kere(&defter, "alan-govdesi");
    println!("Nokta (3 alan): {} govde kosumu", sonraki - onceki);

    let onceki = sonraki;
    let cizgi = Cizgi {
        bas: Nokta { x: 0, y: 0, z: 0 },
        bitis: Nokta { x: 5, y: 5, z: 5 },
    };
    let _ = cizgi.dizilestir(&defter);
    let sonraki = kere(&defter, "alan-govdesi");
    println!("Cizgi (2 nokta, 6 alan): {} govde kosumu", sonraki - onceki);

    let onceki = sonraki;
    let ucgen = Ucgen {
        kenar1: Cizgi {
            bas: Nokta { x: 0, y: 0, z: 0 },
            bitis: Nokta { x: 1, y: 1, z: 1 },
        },
        kenar2: Cizgi {
            bas: Nokta { x: 1, y: 1, z: 1 },
            bitis: Nokta { x: 2, y: 2, z: 2 },
        },
        kenar3: Cizgi {
            bas: Nokta { x: 2, y: 2, z: 2 },
            bitis: Nokta { x: 0, y: 0, z: 0 },
        },
    };
    let _ = ucgen.dizilestir(&defter);
    let sonraki = kere(&defter, "alan-govdesi");
    println!("Ucgen (3 cizgi, 18 alan): {} govde kosumu", sonraki - onceki);

    println!("toplam alan-govdesi kosumu: {}", kere(&defter, "alan-govdesi"));
}
```

```
Nokta (3 alan): 3 govde kosumu
Cizgi (2 nokta, 6 alan): 6 govde kosumu
Ucgen (3 cizgi, 18 alan): 18 govde kosumu
toplam alan-govdesi kosumu: 27
```

**YN11.** `Nokta` üç alan taşıyor ve `dizilestir`i üç kez `alan-govdesi` kaydı düşürüyor — bire
bir. `Cizgi` kendi alanına hiç dokunmuyor, yalnız iki `Nokta`nın `dizilestir`ini çağırıyor; ama
her çağrı kendi üç kaydını taşıdığı için toplam altıya çıkıyor. `Ucgen` aynı örüntüyü bir kat
daha derine taşıyor: üç `Cizgi`nin her biri altı kayıt bırakıyor, toplam on sekiz. Alan sayısı
ile gövde koşum sayısı arasındaki bağıntı burada bire birdir — ama bu bağıntı yalnız yaprak
düzeydeki asıl alanlar (`x`, `y`, `z`) için geçerli; `Cizgi` ve `Ucgen`in kendi gövdeleri hiç
`alan-govdesi` kaydı bırakmıyor, yalnız taşıdıkları alt yapılara devrediyor. İç içe geçme,
sayıyı toplamsal değil çarpımsal büyütüyor: `Ucgen`in üç kenarı, her kenarın altı kaydını
çarpıyor.

#### Ters Yönde Çözümleme: Bir Alanın Başarısızlığı Sonrakini Durdurur

Aynı soru ters yönden de sorulabilir: bir dizge, alan başına kaç gövde koşturarak bir yapıya
geri çevrilir? Çözümleme de elle yazılıyor — her alan `alani_oku` adlı ortak bir işlevden
geçiyor; bu işlev önce bir kayıt düşürüyor, sonra dizge parçasını ayrıştırmayı deniyor.
`Nokta::ters_dizilestir`, üç alanı sırayla okuyor ve her okuma çağrısının ardına bir soru
işareti koyuyor — Hata Yayılımının Kısa Yazımı dersinde ölçülen erken dönüş burada da geçerli:
bir alan başarısız olursa, soru işareti kalan alanların gövdesini hiç çalıştırmadan işlevden
çıkıyor.

```rust
// gozumleme.rs — ters yon: metinden yapiya, alan basina govde kosumu.
mod kere;
use kere::{kere, say, Defter};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

struct GozumHatasi;

struct Nokta {
    x: i32,
    y: i32,
    z: i32,
}

fn alani_oku(defter: &Defter, ad: &str, anahtar: &str, parca: &str) -> Result<i32, GozumHatasi> {
    say(defter, anahtar);
    let ikili: Vec<&str> = parca.splitn(2, '=').collect();
    if ikili.len() != 2 || ikili[0] != ad {
        return Err(GozumHatasi);
    }
    ikili[1].parse::<i32>().map_err(|_| GozumHatasi)
}

impl Nokta {
    fn ters_dizilestir(defter: &Defter, on_ek: &str, metin: &str) -> Result<Nokta, GozumHatasi> {
        let parcalar: Vec<&str> = metin.split(';').collect();
        if parcalar.len() != 3 {
            return Err(GozumHatasi);
        }
        let x = alani_oku(defter, "x", &format!("{}-x", on_ek), parcalar[0])?;
        let y = alani_oku(defter, "y", &format!("{}-y", on_ek), parcalar[1])?;
        let z = alani_oku(defter, "z", &format!("{}-z", on_ek), parcalar[2])?;
        Ok(Nokta { x, y, z })
    }
}

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

    let gecerli = Nokta::ters_dizilestir(&defter, "gecerli", "x=1;y=2;z=3");
    if let Ok(n) = &gecerli {
        println!("gecerli metin cozumlendi: x={} y={} z={}", n.x, n.y, n.z);
    }
    println!(
        "gecerli-x: {}  gecerli-y: {}  gecerli-z: {}",
        kere(&defter, "gecerli-x"),
        kere(&defter, "gecerli-y"),
        kere(&defter, "gecerli-z")
    );

    let gecersiz = Nokta::ters_dizilestir(&defter, "gecersiz", "x=1;y=degil;z=3");
    println!("gecersiz metin basarili mi: {}", gecersiz.is_ok());
    println!(
        "gecersiz-x: {}  gecersiz-y: {}  gecersiz-z: {}",
        kere(&defter, "gecersiz-x"),
        kere(&defter, "gecersiz-y"),
        kere(&defter, "gecersiz-z")
    );
}
```

```
gecerli metin cozumlendi: x=1 y=2 z=3
gecerli-x: 1  gecerli-y: 1  gecerli-z: 1
gecersiz metin basarili mi: false
gecersiz-x: 1  gecersiz-y: 1  gecersiz-z: 0
```

**YN12.** Geçerli dizgede (`x=1;y=2;z=3`) üç alan da okunuyor ve üç ayrı sayaç birer kez
artıyor — ileri yöndeki bire bir bağıntı burada da geçerli. Geçersiz dizgede (`x=1;y=degil;z=3`)
`x` alanı okunuyor ve başarılı oluyor, `y` alanı da okunuyor ama sayısal olarak ayrıştırılamıyor
— ikisinin de sayacı birer artıyor, çünkü ikisi de en azından denendi. `z` alanının sayacı
sıfırda kalıyor: soru işareti `y`nin başarısızlığını yakaladığı anda işlevden çıkıyor, `z` için
yazılan gövdeye hiç uğranmıyor. Bu, ileri yöndeki her seferinde sınıfının ters yönde koşulsuz
kalmadığını gösteriyor: bir alanın gövdesi, kendisinden önceki alanların başarısına bağlı hale
geliyor.

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

Alan sayısı ile gövde koşum sayısı arasındaki bire bir bağıntı, bu derste kullanılan
yapıların hepsi koşulsuz alanlar taşıdığı için geçerli — her alan her zaman var, her zaman
dönüştürülüyor. Yokluğun Tiple İfadesi dersinde ölçülen yokluk tipi bir alanın kendisi
olsaydı, o alanın dönüşüm gövdesi artık koşulsuz çalışmazdı: bazı değerlerde koşar, bazılarında
hiç koşmazdı ve alan sayısını saymak tek başına gövde koşum sayısını vermezdi. Bu ders bu
durumu koşturmuyor; yalnız bağıntının hangi koşulda geçerli olduğunu, hangi koşulda
bozulacağını söylüyor.

İkinci bir sınır, sayının neyi ölçmediğiyle ilgili. `alan-govdesi` sayacı yalnız gövdenin kaç
kez çağrıldığını sayıyor — ürettiği dizgenin ne kadar uzun olduğunu, ya da bir alanın
dönüşümünün ne kadar pahalı olduğunu ölçmüyor. Tek bir tamsayı alanı ile büyük bir koleksiyon
taşıyan bir alan, bu sayaç için aynı — ikisi de bir kez sayılıyor. Kursun başından beri
sürdürülen bu ayrım burada da geçerli: sayı yalnız kaç kez çalıştığını gösteriyor, ne kadar
sürdüğünü ya da ne ürettiğini değil.

#### Kapanış Tablosu

| Ders | Ölçülen gövde | Sınıfı | Sınırlayıcı ölçüm |
|---|---|---|---|
| Trait Tanımı ve Gerçekleştirimi | Varsayılan trait gövdesi (`ad`) | her seferinde · hiç | Sıfır koşum, gövdenin kaynakta yok olduğu anlamına gelmez |
| Trait Sınırları | Sınırlı ve sınırsız gövdenin koşum sayısı | her seferinde | Bedel derlemede: her tip için ayrı gövde üretimi, koşumla ölçülemez |
| İlişkili Tipler | İlişkili tip / genellik parametresi çağrıları | her seferinde | Fark koşumda değil, çağıranın yazmak zorunda kaldığı bilgide |
| Nesne Güvenliği ve Dinamik Gönderim | `statik-gonderim` / `dinamik-gonderim` | her seferinde | Aynı sayı aynı bedel değil; çağrının nasıl bulunduğu ölçülemez |
| Türetilen Trait'ler | Elle yazılan / türetilmiş eşitlik gövdesi | her seferinde (elle) · sayılamaz (türetilmiş) | Türetilmiş gövde kaynakta yazılı değil, koşum sayısı sayılamaz |
| İşleç Aşırı Yükleme | `islec-govdesi` (`add`) | her seferinde | Çağrı yeri kaynakta görünmez, sayı ancak koşum defteriyle bilinir |
| Yokluğun Tiple İfadesi | Hazır / tembel varsayılan gövde | her seferinde · yalnız gerektiğinde · hiç | Gövde ucuzsa iki yazımın sonucu aynı, fark yalnız sayıda kalır |
| Başarısızlığın Dönüş Değeri | Başarı / başarısızlık kolu | yalnız gerektiğinde · hiç | Kapsanmış kod ile koşmuş kod ayrı olgular |
| Hata Yayılımının Kısa Yazımı | Soru işareti sonrası gövde | yalnız gerektiğinde · hiç | Erken dönüş kalan gövdeyi sessizce atlar, yan etki de gerçekleşmez |
| Özel Hata Tipleri | Hata dönüşümü gövdesi (`From` / `map_err`) | her seferinde | Örtük dönüşüm çağıran bağlamdan bilgi alamaz, kayıp geri dönüşsüz |
| Panik ve Kurtarılamaz Hatalar | Kalan gövde (kurtarılabilir/kurtarılamaz) | her seferinde · hiç | Panik bir hata türü değil tasarım kararı; panik metni basılmadı |
| Kapanışlar | Çağrılmayan kapanış gövdesi | hiç | Kapanışın tanımlanması deftere girmeyen ayrı bir iş |
| Yineleyici Trait'i | Tüketilmeyen zincirin gövdesi | hiç | Tüketilmeyen zincir bedelsiz değil, bedel koşum sayacıyla ölçülemez |
| Yineleyici Bağdaştırıcıları | Tüketilen / erken biten zincir gövdesi | her seferinde · yalnız gerektiğinde | Sıra kaynaktan okunmaz, ancak bir kayıtla gözlemlenir |
| Dizileştirme | Alan başına dönüşüm gövdesi | her seferinde · hiç | Bağıntı yalnız her alan koşulsuzken geçerli; koşullu bir alan sınıfı değiştirir |

#### Özet

- Bir yapının alanlarına uygulanan dönüşüm gövdesi alan başına bir kez koşar; n alan içeren
  bir yapıda bu gövde n kez çalışır — `Nokta`'da üç, iki `Nokta` içeren `Cizgi`'de altı, üç
  `Cizgi` içeren `Ucgen`'de on sekiz kez.
- İç içe geçmiş bir yapıda alan sayısı çarpımsal büyür (3, 6, 18) ve gövde koşum sayısı bu
  büyümeyi birebir izler.
- Bu gövde her seferinde sınıfına girer: her alan, dizileştirme her çağrıldığında kaçınılmaz
  olarak dönüştürülür.
- Ters yönde — metinden yapıya — bir alanın çözümlemesi başarısız olduğunda soru işareti geri
  kalan alanların gövdesini sessizce atlar; bu alanlar o koşumda hiç çalışmaz.
- Bir alan koşulsuz değilse (yokluk tipi taşıyorsa) bu bağıntı bozulur; alan sayısını saymak
  artık gövde koşum sayısını vermez.
- On beş ders boyunca ölçülen bütün gövdeler üç sınıfa (her seferinde, yalnız gerektiğinde,
  hiç) yerleşti; kapanış tablosu bunları tek yerde topluyor.

#### Kurs Kapanışı

Bu kurs on beş ders boyunca tek bir soruyu sürekli farklı düzeneklere uyguladı: kaynakta
yazdığın bu gövde kaç kez koştu? Trait ve Genellikler konusu bu soruyu bir davranışın tipten
ayrılmasına, bir sınırın koşuma bir şey ekleyip eklemediğine, bir sözleşmenin iki farklı
yazımına, bir gönderim biçiminin derleme zamanı mı koşum zamanı mı çözüldüğüne, bir gövdenin
kaynakta hiç yazılı olmadan da koşabileceğine ve bir işlecin arkasında saklı bir çağrıya sordu.
Hata Yönetimi konusu aynı soruyu bir yedek değere, bir başarı/başarısızlık koluna, bir kısa
yazımın arkasındaki dallanmaya, bir hata dönüşümüne ve bir kurtarılamaz duruma taşıdı.
Kapanışlar ve Yineleyiciler konusu ve bu son ders, aynı soruyu bir tanımın kendisine, bir
dolaşım sözleşmesine, bir zincirin ucundaki tüketiciye ve şimdi bir yapının alanlarına
uyguladı. On altı gövdelik ölçüm çekirdeğinin sekizi her seferinde, üçü yalnız gerektiğinde,
beşi hiç koşmadı — bu sayı kurs boyunca değişmedi, yalnız hangi düzeneğin bu sınıflara girdiği
değişti.

Eşzamanlılık ve Asenkron Rust kursu bu soruyu bırakmıyor, öznesini değiştiriyor: bir gövdenin
kaç kez koştuğu yerine, bir işin **ilerlemek için neye ihtiyaç duyduğu** sorulacak — kendi
başına mı ilerliyor, bir yoklayıcı mı ilerletiyor, bir başkasının bitmesini mi bekliyor, yoksa
hiç mi ilerlemiyor. Nesneye Dayalı Java kursunun kararı hangi tipin verdiği sorusunun ve
Arayüzler, Hatalar ve Genellikler kursunun bağın nerede kurulduğu sorusunun yanına, bu kurs kaç
kez koştu sorusunu ekledi; sıradaki kurs bu üçlüye ilerlemek için neye ihtiyaç duyulduğu
sorusunu ekleyecek.

