---
title: Kapanışlar
source: 'https://academia.sh/tr/kurslar/rust-soyutlama/kapanislar'
course: "Trait'ler, Genellikler ve Hata Yönetimi"
language: tr
updated: '2026-09-13T05:09:26+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Aynı işin doğrudan çağrı, gecikmeli çağrı ve hiç çağrılmayan kapanış biçimlerinde kaç kez koştuğunu koşturarak karşılaştırır'
    - 'Taşıma ile yakalanan bir değerin ömrünün kapanışın kendisine bağlandığını, gövdenin koşup koşmamasından bağımsız olarak gösterir'
    - 'Kapanışın tanımlanmasının koşum defterine girmeyen ayrı bir iş olduğunu ayırt eder'
---

# Kapanışlar

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.
