---
title: 'Trait Sınırları'
source: 'https://academia.sh/tr/kurslar/rust-soyutlama/trait-sinirlari'
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 sınırı sözdizimini (tekli, çoklu ve where yan tümcesi) tanımlar ve amacını açıklar'
    - 'Sınırlı bir genel gövde ile aynı işi yapan somut tipli bir gövdenin koşum sayısını karşılaştırarak sınırın koşumda adım eklemediğini gösterir'
    - 'Koşumda ölçülemeyen bir bedelin (her tip için ayrı üretilen gövde) var olduğunu belirtir ve sayıyla iddia etmekten kaçınır'
---

# Trait Sınırları

Bir genel fonksiyonun tip parametresine bir trait sınırı koymak, kaynağa hiç adım eklemez — aynı gövde sınırlı ve sınırsız iki biçimde yazılıp koşturulduğunda ikisi de birebir aynı sayıyı verir; sınırın bedeli koşum defterine değil derlemeye yazılır ve bu sayıyla ölçülemez.

Önceki ders bir trait'in varsayılan gövde taşıyabildiğini ve bu gövdenin hangi tip için
çalıştığının koşumda sayılabildiğini gösterdi. Ama `statik` gibi bir fonksiyonun `<T: Etiket>`
yazımı henüz açıklanmadı — bu, bir genel fonksiyonun tip parametresine konan bir **trait
sınırı**dır ve bu dersin konusu bu sınırın kendisi. Soru aynı kalıyor: kaynakta yazdığın bu
gövde kaç kez koştu? Ama bu kez ölçülen gövde değil, gövdenin etrafına konan **koşul**.

## Sınır Ne Yazıyor

Bir genel fonksiyon, `fn ad<T>(x: &T)` yazımıyla herhangi bir tipi kabul edebilir; ama gövde
içinde `x` üzerinde bir yöntem çağırmak istiyorsa, derleyicinin o yöntemin var olduğunu
bilmesi gerekir. Trait sınırı tam bunu sağlıyor: `fn ad<T: Etiket>(x: &T)` yazımı, "`T` ne
olursa olsun, `Etiket`i gerçekleştirmiş olmalı" diyor. Sınır konmadan `x.defteri()` çağrısı
derlenmez, çünkü derleyici `T` için hangi yöntemlerin var olduğunu bilmiyor; sınır konduktan
sonra derleyici bu bilgiyi trait bildiriminden okuyor.

Sınırsız bir genel fonksiyon, `T` hakkında hiçbir şey bilmiyor demektir. `fn ad<T>(x: &T)`
yazıldığında derleyici `x`e yalnız her tipte ortak olan işlemleri (taşımak, bir referans
almak) uygulamaya izin verir; `x.defteri()` gibi `Etiket`e özgü bir çağrı yazılırsa derleyici
bunu reddeder, çünkü elindeki tek bilgi `T`nin *bir tip olduğu*dur, `Etiket`i
gerçekleştirdiği değil. Bu redd, önceki kurstaki tip anahtarının aksine burada bir çalışma
zamanı davranışı değil — kaynak hiç derlenmiyor. Trait sınırı tam bu boşluğu dolduruyor:
`T: Etiket` yazıldığı andan itibaren derleyici, gövdenin içinde `Etiket`in bildirdiği her
yöntemin `x` üzerinde çağrılabileceğini biliyor ve `x.defteri()` derlenir hale geliyor. Sınır
böylece iki tarafı aynı anda bağlıyor: çağıran tarafı ("bu fonksiyona yalnız `Etiket`i
gerçekleştiren tipler geçirilebilir") ve gövde tarafını ("bu gövde `Etiket`in yöntemlerini
güvenle çağırabilir").

Aynı sınır iki ayrı yerde yazılabilir. Kısa biçim tip parametresinin hemen yanına gelir:
`fn sinirli_govde<T: Etiket>(x: &T)`. Sınır uzadığında (birden fazla trait, uzun tip adları)
aynı anlam bir `where` yan tümcesiyle de yazılabilir:

```rust
// where-bicimi.rs — ayni sinir, imzadan ayrilmis bicimde.
trait Etiket {
    fn ad(&self) -> String;
}

struct Ornek;
impl Etiket for Ornek {
    fn ad(&self) -> String {
        String::from("ornek")
    }
}

fn kisa<T: Etiket>(x: &T) -> String {
    x.ad()
}

fn uzun<T>(x: &T) -> String
where
    T: Etiket,
{
    x.ad()
}

fn main() {
    let o = Ornek;
    println!("kisa={} uzun={}", kisa(&o), uzun(&o));
}
```

```
kisa=ornek uzun=ornek
```

**TR3.** `kisa` ve `uzun` derleyici için tamamen aynı anlama geliyor; `where` yan tümcesi bir
okunabilirlik seçeneği, ayrı bir mekanizma değil. Birden fazla sınır gerektiğinde
(`T: Etiket + Clone` gibi) kısa biçim satırı uzatırken, `where` biçimi her sınırı ayrı bir
satıra koyarak imzayı kısa tutabiliyor. Bu ders ikisini birbirinin yerine kullanıyor;
hangisinin seçildiği koşum sayısını hiç etkilemiyor.

Birden fazla sınır `+` işaretiyle birleştirilir: `T: Etiket + Clone`, "`T` hem `Etiket`i
gerçekleştirmeli hem de `Clone`u" demektir. Bu, önceki derste tanıdık bir yapı: `Etiket`in
kendisi tek bir sözleşmeydi, burada iki sözleşme aynı tip parametresine birden bağlanıyor.
`+` burada bir birleşim değil bir **kesişim** kuruyor: `T` yerine geçebilecek tipler kümesi,
yalnız `Etiket` gerçekleştirenlerin kümesiyle yalnız `Clone` gerçekleştirenlerin kümesinin
kesişimidir. Bir sınır eklemek, hangi tiplerin fonksiyona geçirilebileceğini her zaman
**daraltır**, hiçbir zaman genişletmez; bu yüzden bir imzaya yeni bir sınır eklemek geriye
dönük olarak bazı çağrıları kesebilir, ama hiçbir zaman yeni bir çağrıyı mümkün kılmaz.

Sınırlar, tip parametresinin *nerede* kısıtlandığını da değiştirmiyor: `<T: Etiket>` ile
`where T: Etiket` arasındaki tek fark yerleşimdir, kısıtın gücü değil. İki sınır bir arada
yazıldığında (`T: Etiket + Clone`) da aynı ilke geçerli: derleyici gövdeyi kontrol ederken
`T`nin *hem* `Etiket`in *hem* `Clone`un yöntemlerini taşıdığını varsayıyor; sınırlardan
biri eksik olsaydı, o trait'e ait tek bir çağrı bile gövdeyi derlenemez hale getirirdi.

Bir imza birden fazla tip parametresi taşıyabilir ve her parametrenin kendi sınırı olabilir:
`fn birlestir<T: Etiket, U: Etiket>(a: &T, b: &U)` yazımında `T` ile `U` aynı sınırı
paylaşıyor görünse de derleyici için bağımsız iki parametredir — `a` ile `b` farklı somut
tiplerde olabilir, tek ortak zorunluluk ikisinin de `Etiket`i gerçekleştirmiş olmasıdır.
Sınırlar parametre başına konur; bir parametrenin sınırı diğerini etkilemez, aynı trait adı
iki yerde tekrarlansa bile.

```rust
// birlestir.rs — iki tip parametresi, ayni trait sinirini bagimsiz tasiyor.
trait Etiket {
    fn ad(&self) -> String;
}

struct Sade;
struct Ozel;

impl Etiket for Sade {
    fn ad(&self) -> String {
        String::from("sade")
    }
}
impl Etiket for Ozel {
    fn ad(&self) -> String {
        String::from("ozel")
    }
}

fn birlestir<T: Etiket, U: Etiket>(a: &T, b: &U) -> String {
    format!("{}+{}", a.ad(), b.ad())
}

fn main() {
    let s = Sade;
    let o = Ozel;
    println!("{}", birlestir(&s, &o));
    println!("{}", birlestir(&s, &s));
}
```

```
sade+ozel
sade+sade
```

İkinci çağrıda `T` ve `U` aynı somut tipe (`Sade`) çözülüyor; ilkinde ise ikisi farklı
somut tiplere (`Sade` ve `Ozel`) çözülüyor. `birlestir`in imzası her iki çağrıyı da kabul
ediyor, çünkü sınır yalnız "`Etiket`i gerçekleştirmiş olmalı" diyor, "aynı tip olmalı"
demiyor. `T` ile `U`nun aynı somut tipe çözülmesini zorunlu kılmak isteseydik imza tek bir
parametre kullanırdı (`fn birlestir<T: Etiket>(a: &T, b: &T)`); iki ayrı parametrenin
varlığı, tam olarak bu zorunluluğu **kaldırmak** için orada duruyor.

Trait sınırı yazılmadığında tip parametresi tamamen boş kalmıyor: her `T`, aksi
belirtilmedikçe örtük bir **boyut** sınırı da taşır — derleyici, `T`nin bellekte sabit ve
bilinen bir boyutu olduğunu varsayar, çünkü genel bir gövde `T` tipinde bir değeri
yerelde tutabilmelidir. Bu örtük sınır, `Etiket` gibi açıkça yazılan sınırlardan ayrı bir
katmandır ve çoğu genel fonksiyon için hiç görünmez; yalnız dilim gibi boyutu değişken
tiplerle çalışan az sayıda ileri düzey yazımda açıkça gevşetilmesi gerekir. Bu ders o
gevşetmeyi kullanmıyor — burada önemli olan, sınırın **görünen** kısmının (`Etiket`) yanında
her zaman görünmeyen bir varsayılan sınırın da bulunduğunu bilmek.

## Sınırın Kosumdaki Payı: Sıfır Adım

Aynı işi iki yazımla kurup koşum sayısını karşılaştıralım: biri `<T: Etiket>` sınırıyla genel,
öbürü doğrudan somut tip (`Sade`) alan, sınırsız bir fonksiyon. Üçüncü bir fonksiyon da
birden fazla sınırı (`Etiket + Clone`) birlikte kullanıyor.

```rust
// kere.rs — trait Etiket'in kesiti: `defteri`, tip adi ve iki gerceklestirim.

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 tip(&self) -> &'static str;
}

#[derive(Clone)]
pub struct Sade {
    pub defter: Defter,
}

impl Etiket for Sade {
    fn defteri(&self) -> &Defter {
        &self.defter
    }
    fn tip(&self) -> &'static str {
        "sade"
    }
}

pub struct Ozel {
    pub defter: Defter,
}

impl Etiket for Ozel {
    fn defteri(&self) -> &Defter {
        &self.defter
    }
    fn tip(&self) -> &'static str {
        "ozel"
    }
}
```

```rust
// sinir-govdesi.rs — trait sinirinin kosumda hicbir adim eklemedigini olcer.
mod kere;
use kere::{kere, say, Defter, Etiket, Ozel, Sade};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;

// Ayni kaynak satiri, cagrildigi her somut tip icin derleyici tarafindan
// ayrica uretiliyor - bu, kosum sayacinin gormedigi bir bedeldir.
fn sinirli_govde<T: Etiket>(x: &T) -> u32 {
    let anahtar = format!("sinirli-govde-{}", x.tip());
    say(x.defteri(), &anahtar);
    kere(x.defteri(), &anahtar)
}

// Ayni is, sinirsiz: dogrudan somut tip aliyor, tip parametresi yok.
fn somut_govde(x: &Sade) -> u32 {
    say(x.defteri(), "somut-govde");
    kere(x.defteri(), "somut-govde")
}

// T hem Etiket'i hem Clone'u sagliyor olmali - iki sinir '+' ile birlesiyor.
fn kopyala_ve_say<T: Etiket + Clone>(x: &T) -> T {
    say(x.defteri(), "coklu-sinir-govdesi");
    x.clone()
}

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 _ = sinirli_govde(&sade);
    let _ = sinirli_govde(&sade);
    let _ = sinirli_govde(&ozel);

    let _ = somut_govde(&sade);
    let _ = somut_govde(&sade);

    let kopya = kopyala_ve_say(&sade);
    let _ = kopyala_ve_say(&kopya);

    let mut govdeler: BTreeMap<&str, u32> = BTreeMap::new();
    govdeler.insert("sinirli-govde-sade", kere(&defter, "sinirli-govde-sade"));
    govdeler.insert("sinirli-govde-ozel", kere(&defter, "sinirli-govde-ozel"));
    govdeler.insert("somut-govde", kere(&defter, "somut-govde"));
    govdeler.insert("coklu-sinir-govdesi", kere(&defter, "coklu-sinir-govdesi"));

    println!("{:<22}{}", "govde", "kac kez");
    for (ad, n) in &govdeler {
        println!("{:<22}{}", ad, n);
    }
}
```

```
govde                 kac kez
coklu-sinir-govdesi   2
sinirli-govde-ozel    1
sinirli-govde-sade    2
somut-govde           2
```

**TR4.** `sinirli-govde-sade` iki kez koştu, `somut-govde` de iki kez — birebir aynı sayı.
İkisi de aynı işi (defteri okuyup bir satırı saymak) yapıyor; biri genel bir tip parametresi
üzerinden, öbürü doğrudan somut tip üzerinden. Trait sınırı, gövdenin **kaç kez** çağrıldığına
hiçbir şey eklemiyor ya da çıkarmıyor — bu, sınırın koşum defterinde hiçbir iz bırakmadığını
gösteriyor. Sınır bir **derleme zamanı** kuralıdır: hangi tiplerin `sinirli_govde`ye
geçirilebileceğini belirler, ama geçirildikten sonra çalışan kod, sınır hiç yokmuş gibi
davranır.

Arayüzler, Hatalar ve Genellikler kursu Go'nun genelliklerinde **bağın kullanım yerinde
kurulduğunu** ölçmüştü — bir genel fonksiyonun tip kısıtı, çağrının hangi tiplerle
yapılabileceğini kullanım yerinde sınırlıyordu. Bu kurs aynı soruyu sormuyor; burada ölçülen,
o sınırın **koşuma ne eklediği** — yanıt hiçbir şey. `sinirli-govde-ozel` satırı bunu ikinci
bir açıdan doğruluyor: aynı `sinirli_govde` fonksiyonu `Ozel` ile de çağrılabiliyor (bir kez),
çünkü `Ozel` de `Etiket`i gerçekleştiriyor; sınır, hangi tiplerin kabul edileceğini
belirliyor, ama kabul edilen her tip için gövde aynı biçimde çalışıyor.

`coklu-sinir-govdesi` de aynı sınıfa giriyor: iki kez çağrıldı, iki kez koştu. Burada
`T: Etiket + Clone` sınırı iki ayrı koşulu birleştiriyor, ama gövdenin kendisi yine tek bir
sayma çağrısı ve bir kopyalama işlemi. Sınırın sayısı arttıkça (`Etiket`, sonra
`Etiket + Clone`) koşum defterindeki satır sayısı artmıyor; sınır tip parametresine konan bir
koşuldur, gövdenin içine eklenen bir adım değildir.

Dört satırın tamamı **her seferinde** sınıfına giriyor: hiçbiri "yalnız gerektiğinde" ya da
"hiç" değil, çünkü her çağrı kaçınılmaz olarak karşılığını buluyor — atlanan, ertelenen ya da
tüketilmeyen bir adım yok. Bu, önceki dersteki `varsayilan-govde-sade` ve
`yeniden-yazilan-govde` satırlarıyla aynı sınıf: farklı olan yalnız gövdenin hangi koşulla
çağrılabildiği, gövdenin kendisinin çalışıp çalışmadığı değil.

Bu eşitlik, kursun ilerleyen bir dersinde farklı bir soruyla yeniden karşımıza çıkacak. Nesne
Güvenliği ve Dinamik Gönderim dersi, aynı `Etiket` sözleşmesini bu kez `<T: Etiket>` sınırı
yerine `&dyn Etiket` ile kullanan bir çağrı biçimiyle karşılaştıracak. Orada da sayı eşit
çıkıyor — ama nedeni burada görülenden farklı: sınırlı genellik çağrıyı derleme zamanında
çözerken, `dyn` biçimi aynı çağrıyı koşum sırasında bir yöntem tablosundan buluyor. Bu ders
yalnız sınırın **varlığının** koşuma bir şey eklemediğini gösteriyor; iki çağrı biçiminin
birbirinden nasıl ayrıldığı ayrı bir derste ele alınacak.

`somut_govde` fonksiyonunun hiç sınıra ihtiyacı olmaması da aynı ilkenin bir sonucu. Bir
fonksiyon zaten tek bir somut tipe (`Sade`) bağlandığında "hangi tipler kabul edilsin"
sorusunun yanıtı baştan bellidir — yalnız `Sade`. Trait sınırı, bu sorunun yanıtının
**birden fazla** tip olabildiği durumlar için var; tek tipe bağlı bir fonksiyonda sormaya
gerek bile yok. Bu yüzden `somut_govde` ile `sinirli_govde` arasındaki fark bir eksiklik
değil, iki farklı sorunun karşılığı: biri "bu iş için doğru tip hangisi" sorusunu hiç
sormuyor, öbürü bu soruyu sınırla yanıtlıyor.

## Sınırlayıcı Ölçüm

Koşumda bedeli olmaması, sınırın **bedelsiz** olduğu anlamına gelmiyor. `sinirli_govde`
fonksiyonu `Sade` ve `Ozel` için ayrı ayrı çağrıldığında, derleyici bu fonksiyonun **her somut
tip için ayrı bir gövde** ürettiği bir yöntemle çalışıyor: kaynakta tek bir `fn
sinirli_govde<T: Etiket>` satırı var, ama üretilen ikili dosyada `Sade` için bir gövde,
`Ozel` için başka bir gövde bulunuyor. Bu üretim derleme sırasında olur ve koşum defterine
hiç girmez — defter yalnız çalışma zamanındaki çağrıları sayıyor, derleme zamanında üretilen
gövde sayısını değil. Bu kursun ölçüm yöntemi bu bedeli **gösteremiyor**; ders bunu açıkça
söylüyor ve saymaya kalkmıyor. `sinirli-govde-sade` ile `sinirli-govde-ozel`in toplamda üç
kez koşması, arkada kaç ayrı gövdenin üretildiğiyle ilgili hiçbir bilgi taşımıyor.

Bu sınır, kursun ölçüm yönteminin genel bir sınırını da ortaya koyuyor: koşum defteri yalnız
**çalışma zamanında gerçekten geçilen** satırları sayabiliyor. Derleme zamanında olan
hiçbir şey — kaç ayrı gövde üretildiği, hangi tipin hangi sırayla derlendiği, bir sınırın
kaç aday tipi elediği — bu defterle görünür kılınamaz. Kurs boyunca "sayı" sözcüğü hep
**çalışma zamanı sayısı** anlamına geliyor; derleme zamanına ait bir soru sorulduğunda ders
bunu açıkça söyleyip düzyazıyla yanıtlıyor, bir sayı uydurmuyor. Bu ayrım, ölçümün neyi
kapsayıp neyi kapsamadığını baştan çizmek için gerekli: kursun geri kalanında "bu satır
hiç koşmadı" cümlesi her zaman koşum zamanına ait bir gözlemdir, kaynağın var olup olmadığına
dair bir iddia değildir.

## Özet

- Trait sınırı, bir genel fonksiyonun tip parametresine "bu trait'i gerçekleştirmiş olmalı"
  koşulunu koyar; kısa biçim (`<T: Etiket>`) ve `where` yan tümcesi derleyici için aynı
  anlama gelir.
- Birden fazla sınır `+` ile birleştirilir (`T: Etiket + Clone`); gövde tek kalır, sınır
  sayısı arttıkça çalışan kod artmaz.
- Sınırlı bir gövde (`sinirli-govde-sade`) ile aynı işi yapan sınırsız bir gövde
  (`somut-govde`) her seferinde aynı sayıda koştu: ikisi de iki kez — sınır koşum defterine
  hiçbir adım eklemiyor.
- Sınırın gerçek bedeli koşumda değil derlemede: her somut tip için ayrı bir gövde üretiliyor,
  ama bu üretim koşum sayacıyla ölçülemez ve ders bunu ölçmeye kalkmıyor.

## Sonraki Adım

Sınır, bir trait'i tip parametresine dıştan bağladı. Bir trait bazen sözleşmenin **kendi
içinde** bir tip taşımak isteyebilir — çağıranın hiç belirtmediği, gerçekleştirenin
belirlediği bir tip. Sıradaki ders bu ilişkili tipi, aynı sözleşmeyi bir de tip parametresiyle
yazarak karşılaştırıyor.
