---
title: 'İlişkili Tipler'
source: 'https://academia.sh/tr/kurslar/rust-soyutlama/iliskili-tipler'
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:
    - 'İlişkili tip sözdizimini tanımlar ve bunu aynı sözleşmenin genellik parametresiyle yazılmış biçiminden ayırır'
    - 'İki yazımın koşum sayısının aynı çıktığını, farkın çağıranın yazmak zorunda kaldığı bilgide olduğunu gösterir'
    - 'İlişkili tipin bir tip için tek gerçekleştirime izin veren bir kısıt olduğunu, genellik parametresinin buna izin vermediğini karşılaştırarak açıklar'
---

# İlişkili 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?
