---
title: 'Döngüsel Referanslar'
source: 'https://academia.sh/tr/kurslar/sahiplik/dongusel-referanslar'
course: 'Sahiplik ve Bellek Modeli'
language: tr
updated: '2026-10-21T05:03:44+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'İki değerin Rc üzerinden birbirini tuttuğunda güçlü sayacın hiç sıfıra inmediğini ve defterde kayıt oluşmadığını koşturarak gösterir'
    - 'Bir zayıf referansın güçlü sayacı artırmadan döngüsel referansı kırdığını ve kırıldıktan sonra defterde iki kaydın oluştuğunu kanıtlar'
    - 'Güçlü ve zayıf referans sayaçlarının aynı değer üzerinde bağımsız izlendiğini örnekleyerek ayırt eder'
---

# Döngüsel Referanslar

İki değer RefCell üzerinden birbirini Rc ile tuttuğunda güçlü sayacın hiçbir zaman sıfıra inmediği ve defterde hiçbir kaydın oluşmadığı ölçülüyor — kursun tek sızıntısı. Bir zayıf referansın sayacı artırmadan aynı döngüsel referansı kırdığı ve kırıldıktan sonra defterde iki kaydın birden oluştuğu koşturuluyor; güçlü ve zayıf sayaç değerleri basılıyor.

Kursun üç konusu ve on üç dersi boyunca bir değerin ömrü hep dört sınıftan birine düştü ve
sıfırıncı bir seçenek hiç görülmedi — `Rc` ile `Arc`ın sayacı bile, kaç kez kopyalanırsa
kopyalansın, er ya da geç sıfıra indi. Bu son ders tam bu varsayımı bozan tek durumu ölçüyor:
iki değer, `RefCell` üzerinden birbirinin içine birer `Rc` koyarsa, güçlü sayaç hiçbir zaman
sıfıra inmiyor ve defterde hiçbir kayıt oluşmuyor. Kursun on üç dersi boyunca ölçülen her
bitiş kaynaktan okunabilir, belirlenimci bir noktaya sahipti; bu ders o belirlenimciliğin
kendi sınırını — bir bitişin hiç gerçekleşmeyebileceğini — gösteriyor.

Eşzamanlılık kursunda (M09/K03) ölçülen sızıntı, durması beklenen ama hiç durmayan bir
goroutine'di: koşan bir birim sızıyordu. Burada sızan bir birim değil bir değer — kaynak
kodun kendisi tamamen sıradan biçimde biter, program sonlanır, ama iki `Dugum` değeri bu
sondan sonra da bellekte kalır, hiçbir zaman bırakılmaz.

## İki Değer Birbirini Tutunca

Çekirdeğin `Defter`, `Iz` ve `Drop` uygulaması burada da aynı kesitle kullanılıyor. Üstüne
tek bir yeni tip ekleniyor: `Dugum`, kendi `Iz`sini ve bir sonraki düğüme güçlü bir referans
tutan `sonraki: RefCell<Option<Rc<Dugum>>>` alanını taşıyor. İki `Dugum` değeri (`a`, `b`)
`Rc::new` ile kuruluyor, sonra her biri ötekine `Rc::clone` ile bir referans veriyor —
Referans Sayımı dersinde ölçülen kopyalama işleminin ta kendisi, burada
yalnızca karşılıklı yönde uygulanıyor.

**PS13.** — "iki değerin birbirini tuttuğunda güçlü sayacın kapsam kapandıktan sonra bile
sıfıra inmediği" iddiası, aşağıdaki koşumun `kapsam kapaninca defter uzunlugu: 0`
satırıyla doğrulanıyor.

```rust
// dongu_olusumu.rs — iki degerin birbirini tutmasi: sayac hic sifira inmiyor, defter bos kaliyor
use std::cell::RefCell;
use std::rc::Rc;

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

struct Iz {
    ad: String,
    defter: Defter,
}

impl Iz {
    fn yeni(ad: &str, defter: &Defter) -> Iz {
        Iz { ad: ad.to_string(), defter: Rc::clone(defter) }
    }
}

impl Drop for Iz {
    fn drop(&mut self) {
        self.defter.borrow_mut().push(self.ad.clone());
    }
}

struct Dugum {
    iz: Iz,
    sonraki: RefCell<Option<Rc<Dugum>>>,
}

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

    {
        let a = Rc::new(Dugum {
            iz: Iz::yeni("dugum-a", &defter),
            sonraki: RefCell::new(None),
        });
        let b = Rc::new(Dugum {
            iz: Iz::yeni("dugum-b", &defter),
            sonraki: RefCell::new(None),
        });

        *a.sonraki.borrow_mut() = Some(Rc::clone(&b));
        *b.sonraki.borrow_mut() = Some(Rc::clone(&a));

        println!("a adi: {}, sayac: {}", a.iz.ad, Rc::strong_count(&a));
        println!("b adi: {}, sayac: {}", b.iz.ad, Rc::strong_count(&b));
    }

    println!("kapsam kapaninca defter uzunlugu: {}", defter.borrow().len());
}
```

```
a adi: dugum-a, sayac: 2
b adi: dugum-b, sayac: 2
kapsam kapaninca defter uzunlugu: 0
```

Kap dolu iken sayaç ikisinde de iki: `a`nın kendi bağlaması bir, `b.sonraki`nin tuttuğu
kopya bir; `b` için de simetrik. Kapsam kapanınca `a` ve `b` bağlamaları bildirimin tersi
sırayla bitiyor — Yığıt ve Öbek dersinde kurulan kural burada da geçerli. Ama her
bağlamanın bitmesi yalnızca kendi sayacını bir azaltıyor: `b` bittiğinde `a.sonraki`nin
tuttuğu kopya hâlâ `b`nin `Dugum`ını canlı tutuyor; `a` bittiğinde de `b.sonraki`nin
tuttuğu kopya `a`nınkini. Geriye kalan sayaç ikisinde de bir — hiçbir zaman sıfır olmuyor.
`Dugum::iz` alanının `Drop`u bu yüzden hiç tetiklenmiyor, çünkü `Dugum`ın kendisi hiç
bırakılmıyor; defter, koşumun sonuna kadar boş kalıyor.

## Zayıf Referans Sayacı Artırmıyor, Döngüsel Referansı Kırıyor

Bu döngüsel referansı kırmanın yolu, iki yönden birini **zayıf** bir referansa çevirmek:
`Weak<T>`, `Rc<T>` gibi aynı değere işaret eder ama güçlü sayacı hiç artırmaz. `Dugum`
burada ikiye ayrılıyor — `guclu_sonraki` bir yönü güçlü tutuyor, `zayif_onceki` öbür yönü
zayıf tutuyor. Hangi yönün zayıf olacağı bir tasarım kararı; dilin kendiliğinden verdiği
bir yanıt değil.

**PS14.** — "bir zayıf referansın güçlü sayacı hiç artırmadığı" iddiası, aşağıdaki koşumun
`a adi: kirilan-a, guclu: 1, zayif: 1` satırıyla doğrulanıyor: `b`, `a`ya zayıf referans
tutuyor ve `a`nın güçlü sayacı hâlâ bir.

```rust
// zayif_ile_kirma.rs — zayif referans sayaci artirmiyor ve dongusel referansi kiriyor
use std::cell::RefCell;
use std::rc::{Rc, Weak};

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

struct Iz {
    ad: String,
    defter: Defter,
}

impl Iz {
    fn yeni(ad: &str, defter: &Defter) -> Iz {
        Iz { ad: ad.to_string(), defter: Rc::clone(defter) }
    }
}

impl Drop for Iz {
    fn drop(&mut self) {
        self.defter.borrow_mut().push(self.ad.clone());
    }
}

struct Dugum {
    iz: Iz,
    guclu_sonraki: RefCell<Option<Rc<Dugum>>>,
    zayif_onceki: RefCell<Weak<Dugum>>,
}

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

    {
        let a = Rc::new(Dugum {
            iz: Iz::yeni("kirilan-a", &defter),
            guclu_sonraki: RefCell::new(None),
            zayif_onceki: RefCell::new(Weak::new()),
        });
        let b = Rc::new(Dugum {
            iz: Iz::yeni("kirilan-b", &defter),
            guclu_sonraki: RefCell::new(None),
            zayif_onceki: RefCell::new(Weak::new()),
        });

        *a.guclu_sonraki.borrow_mut() = Some(Rc::clone(&b));
        *b.zayif_onceki.borrow_mut() = Rc::downgrade(&a);

        println!("a adi: {}, guclu: {}, zayif: {}", a.iz.ad, Rc::strong_count(&a), Rc::weak_count(&a));
        println!("b adi: {}, guclu: {}, zayif: {}", b.iz.ad, Rc::strong_count(&b), Rc::weak_count(&b));

        let geri_donen = b.zayif_onceki.borrow().upgrade();
        println!("b uzerinden a'ya geri donus basarili mi: {}", geri_donen.is_some());
    }

    println!("kapsam kapaninca defter: [{}]", defter.borrow().join(", "));
}
```

```
a adi: kirilan-a, guclu: 1, zayif: 1
b adi: kirilan-b, guclu: 2, zayif: 0
b uzerinden a'ya geri donus basarili mi: true
kapsam kapaninca defter: [kirilan-a, kirilan-b]
```

`a`nın güçlü sayacı bir kalıyor, çünkü yalnızca kendi bağlaması ona sahip; `b`nin zayıf
sayacı bir, çünkü `b.zayif_onceki` ona bakıyor ama sahiplenmiyor. `b`nin güçlü sayacı ise
iki — kendi bağlaması ve `a.guclu_sonraki`nin tuttuğu kopya. `upgrade()` çağrısı, zayıf
referansın hâlâ geçerli bir değere baktığını doğruluyor: bir `Option<Rc<Dugum>>`
döndürüyor ve `a` hâlâ yaşadığı için sonuç `Some`.

**PS15.** — "bu döngüsel referans kırıldıktan sonra defterde iki kaydın birden oluştuğu"
iddiası, aynı koşumun son satırındaki `defter: [kirilan-a, kirilan-b]` ile doğrulanıyor.

Kapsam kapanınca `b` önce bitiyor ve güçlü sayacını ikiden bire indiriyor — `a.guclu_sonraki`
hâlâ bir kopya tuttuğu için `b`nin `Dugum`ı henüz bırakılmıyor. `a` bitince kendi sayacı
sıfıra iniyor ve gerçekten bırakılıyor; bu bırakma önce kendi `iz` alanını (`kirilan-a`)
deftere yazıyor, sonra kendi `guclu_sonraki` alanını bırakırken `b`ye olan son güçlü
referansı da düşürüyor — `b`nin sayacı da sıfıra iniyor ve `b` de bırakılıyor
(`kirilan-b`). Zayıf referans bu zincire hiç girmiyor: `b.zayif_onceki` bırakılırken hiçbir
güçlü sayaç değişmiyor, çünkü zaten hiç saymamıştı.

## Sınırlayıcı Ölçüm: Derleyici Döngüsel Referansı Görmüyor

Yukarıdaki iki koşum arasındaki fark, kaynakta hiçbir redd olmadan geçiyor. `dongu_olusumu.rs`
da, `zayif_ile_kirma.rs` de aynı satırlarla derleniyor: `Rc::new`, `Rc::clone`, bir
`RefCell` alanına yazma — Referans Sayımı dersinden beri (PS5) kullanılan işlemlerin ta
kendisi. Ödünç Denetleyicisi dersi, denetleyicinin sahiplik disiplinini sınadığını ama
kodun doğruluğunu güvence altına almadığını göstermişti; burada aynı sınır bir adım daha
genişliyor: derleyici, iki `Rc` değerinin birbirini tuttuğunu hiçbir zaman fark etmiyor,
çünkü her `Rc::clone` çağrısı kendi başına geçerli bir işlem — sayaç artırma, hangi değere
işaret ettiğine bakmaksızın izin veriliyor.

**PS16.** — "birbirini tutan bir kaynağın, birbirini tutmayan bir kaynakla aynı derleme
yolundan geçtiği" iddiası, iki koşumun da (`dongu_olusumu`, `zayif_ile_kirma`) hiçbir uyarı
ya da hata vermeden derlenmesiyle doğrulanıyor.

Bu, kursun bütün derslerinde ölçülen belirlenimciliğin bir istisnası değil, kendi sınırı:
bitiş noktası hâlâ kaynaktan okunabiliyor, yalnız bu kez okunan şey bir bitişin **hiç yer
almayacağı**. Döngüsel referansı kırmak bu yüzden derleyicinin değil yazanın sorumluluğu;
hangi yönün zayıf olacağını seçmek de öyle — `b`nin `a`yı zayıf tutması yerine `a`nın
`b`yi zayıf tutması seçilseydi aynı kırılma ters yönde gerçekleşirdi, sonuç değişmezdi.

## Kapanış Tablosu

On dört ders boyunca ölçülen on dört bitiş noktası aşağıda toplanıyor. Her satırın sınıfı
ilgili dersin kendi metninde yazılıdır; tablo yeni bir sınıflama yapmıyor, dağınık olanı bir
araya getiriyor.

| Ders | Ölçülen değer | Ömrün bittiği yer | Sınırlayıcı ölçüm |
|---|---|---|---|
| Yığıt ve Öbek | `kapsam-1/2/3` bitiş sırası | kapsam sonu | bitiş sırası bildirimin tam tersi |
| Sahiplik ve Taşıma | `islev-icine`, `ic-kapsama`, `yapi-icine` | taşındığı yerde | taşıma kopyalama değil, defterde tek kayıt |
| Ödünç Alma | `odunc-1` (paylaşımlı + değiştirilebilir ödünç) | kapsam sonu | ödünç ömrü uzatmaz, referans kaynağı aşamaz |
| Dilimler | `goruntu` (liste dilimi) | kapsam sonu | dilim yalnız aralığı değil kaynağın tamamını kilitler |
| Ödünç Denetleyicisi | `ekle_ve_say` dönüş değeri | kapsam sonu | denetleyici doğruluğu değil erişim disiplinini sınar |
| Yaşam Süresi Kavramı | `r1`, `r2`, `r3` referans aralığı | kapsam sonu | iki bağımsız aralık iç içe olmak zorunda değil |
| Açık Yaşam Süresi Açıklamaları | `oncelikli_ad`, `digerinin_adi` | kapsam sonu | açıklama bir istektir, sözü tutmayan gövde reddedilir |
| Yaşam Süresi Çıkarım Kuralları | `ilk_uc_harf`, `daha_uzun_olan` | kapsam sonu | çıkarım kolaylıktır, kapsamadığı imzada açıklama zorunlu |
| Yapılarda Referans Tutma | `SahipYapi`, `OduncYapi` | taşındığı yerde | yapı, yaşam süresi parametresi olmadan referans taşıyamaz |
| Öbekte Tekil Sahiplik | `obekteki-tekil`, `liste-1..3` | kabıyla birlikte | aynı tip iki farklı sınıfa düşebilir, sınıf işlemden gelir |
| Referans Sayımı | `paylasilan` (Rc) sayacı: 1, 2, 3, 2 | kabıyla birlikte | bitişi belirleyen kopyanın kimliği değil kalan sayısı |
| Atomik Referans Sayımı | `paylasilan` (Arc) sayacı: 1, 2, 3, 2 | kabıyla birlikte | atomik sayaç veriyi değil yalnız sayacı korur |
| İç Değişebilirlik | `sayac` (RefCell) ödünç denemeleri | kabıyla birlikte | kural kaybolmuyor, çalışma zamanında `Err`e dönüşüyor |
| Döngüsel Referanslar | `dugum-a`, `dugum-b` (birbirini tutan) | hiç bitmedi | derleyici görmüyor, aynı `Rc::clone` hem paylaşımı hem sızıntıyı kurar |

## Özet

- İki değer `Rc` ile birbirini tuttuğunda güçlü sayaç hiçbir zaman sıfıra inmiyor; defterde
  kayıt oluşmuyor — kursun tek sızıntısı.
- Zayıf referans (`Weak<T>`) aynı değere işaret eder ama güçlü sayacı hiç artırmaz; bir
  yönü zayıf tutmak döngüsel referansı kırmaya yetiyor.
- Döngüsel referans kırıldıktan sonra kapsam kapanınca her iki değer de bırakılır ve
  defterde iki kayıt birden oluşur.
- Derleyici bu döngüsel referansı hiçbir zaman reddetmiyor; `Rc::clone` ve `RefCell`
  yazımı, karşılıklı olmayan kullanımla aynı derleme yolundan geçiyor.
- M09/K03'te sızan şey koşan bir birimdi, burada sızan bir değer; ikisi de dışarıdan bir
  sinyal gelmeden kendiliğinden sona ermiyor.

## Kurs Kapanışı

On dört ders boyunca bu kurs tek bir soruyu sordu: bir değerin ömrü nerede bitiyor? Yanıt her
zaman kaynaktan okunabilir kaldı — kapsam sonunda, taşındığı yerde, açık bir çağrıda, bir
kabıyla birlikte ya da, bu derste görüldüğü gibi, hiç. Kursun ilk konusu, Sahiplik Kuralları,
bu sınıfları ve onları koruyan ödünç denetleyicisini kurdu; ikinci konusu, Yaşam Süreleri, bu
denetimin adını (yaşam süresi) ve ne zaman yazılması gerektiğini verdi; üçüncü konusu tekliği
aşıp sahipliği sayılabilir, paylaşılabilir ve en sonunda değiştirilebilir hâle getirdi — ama
her adımda bitiş noktası hâlâ belirlenimciydi, ta ki bu derste iki değerin birbirini tuttuğu
görülene kadar.

Kursun on üç dersi boyunca bir kavram yalnızca adıyla geçti ve tanımlanmadan bırakıldı: `Drop`
trait'i. Kapanış (closure) ve yineleyici (iterator) bu kursta hiç geçmedi; üçü de aynı soruyu
paylaşıyor — bir değerin **ne** yapabildiği, ne kadar yaşadığından ayrı bir soru — ve sıradaki
kurs tam bu soruyu açıyor.
