---
title: 'Nesne Güvenliği ve Dinamik Gönderim'
source: 'https://academia.sh/tr/kurslar/rust-soyutlama/nesne-guvenligi-ve-dinamik-gonderim'
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:
    - 'Statik ve dinamik gönderimin sözdizimini (trait sınırı ile dyn trait nesnesi) ayırt eder'
    - 'İki gönderim biçiminin koşum sayısını karşılaştırarak sayının değişmediğini gösterir'
    - 'Nesne güvenliği koşulunu düzyazıyla tarif eder ve bu koşulu sağlayan kabul edilmiş bir tasarımı koşturarak gösterir'
---

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

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.
