---
title: 'Trait Tanımı ve Gerçekleştirimi'
source: 'https://academia.sh/tr/kurslar/rust-soyutlama/trait-tanimi-ve-gerceklestirimi'
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:
    - 'Trait tanımının bir davranış sözleşmesi kurduğunu ve varsayılan gövde taşıyabildiğini gösterir'
    - 'Ölçüm çekirdeğini koşturarak varsayılan bir gövdenin bir tip için her seferinde, onu yeniden yazan tip için hiç koşmadığını doğrular'
    - 'trait ile arayüz kavramlarını birbirinden ayırır ve M09/K02 ile ölçü eksenindeki farkı açıklar'
---

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

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.
