---
title: 'Arayüzler, Hatalar ve Genellikler'
source: 'https://academia.sh/tr/kurslar/go-tip-sistemi'
language: tr
updated: '2026-08-23T17:00:35+00:00'
license: 'CC BY-SA 4.0'
lessons: 13
outcomes:
    - 'Arayüzleri örtük gerçekleştirim modeliyle tasarlar'
    - 'Hataları sarmalayarak bağlam kaybetmeden yukarı taşır'
    - 'Genelliklerle tip kısıtları tanımlar'
    - 'Bir bağın bildirimde mi kullanım yerinde mi değerde mi kurulduğunu ölçerek ayırır'
---

# Arayüzler, Hatalar ve Genellikler

Go'da bir tipin bir arayüzü karşıladığı hiçbir yerde yazmaz: kurs her düzeneği tek bir soruyla ölçer — bu bağ nerede kuruluyor, kaynaktaki bir bildirimde mi, derleyicinin baktığı kullanım yerinde mi, yoksa çalışma zamanında değerin içinde mi? Arayüzler, hata değerleri ve genellikler aynı çerçeveden geçirilir.

## Arayüzler

Bir tipin arayüzü karşıladığının kaynakta hiçbir yerde yazmaması ve küçük arayüz ilkesinin bunun sonucu olması, boş arayüzün tip bilgisini çalışma zamanına taşıması ve bunun bedeli, tip iddiası ile tip anahtarının değere sorduğu soru, gömmeyle sözleşme bileşimi, arayüzün tüketici tarafında tanımlanmasının ölçülebilir karşılığı.

### Arayüz Kavramı

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/arayuz-kavrami

On altı bağın 4'ü bildirimde, 4'ü kullanım yerinde, 8'i değerin içinde kuruluyor: kursun bağlayıcı dağılımı burada üretiliyor. Aynı tip dört arayüzü karşılıyor ve kaynağında sıfır atıf var; küçük arayüz ilkesi burada bir öğüt değil, yöntem sayısı arttıkça karşılayan tip sayısının düşmesiyle ölçülen bir gözlem. Sınırlayıcı ölçüm: derleme zamanı uyum kanıtı aynı bağı bildirime taşıyor, kanıt yokken derleyici sessiz kalıyor.

Go Temelleri kursu bir adın görünürlüğünü, bir bağımlılığın sürümünü ve bir biçim kuralının
uygulanmasını kimin belirlediğini sorarak kapandı — üçünün de yanıtı dilin kendisiydi: dışa
aktarma bir harfin büyüklüğüne, sürüm seçimi modül dosyasının yazdığı en düşük isteğe, biçim
kuralı biçimlendiricinin kendisine bağlıydı. Bu ders aynı soruyu bir adım içeri taşıyor. Bir
tip bir arayüzü karşıladığında, bunu söyleyen tek bir satır yoktur — ne arayüzün tanımında, ne
tipin kendi kaynağında. Derleyici bu bağı biliyor, ama kaynağı okuyan biri onu göremiyor.
Öyleyse bu bağ nerede kuruluyor?

Bu kurs bu soruyu on altı kez soruyor — arayüzlerde, hata değerlerinde, genelliklerde — ve her
seferinde yanıtı üç yerden birine yazıyor: bağ kaynakta açıkça mı yazılı (**bildirim**),
derleyici onu bir değerin kullanıldığı yerde mi kuruyor (**kullanım yeri**), yoksa bağ çalışma
zamanında değerin kendisinde mi taşınıyor (**değer**). Bu ders bu üç yeri ilk kez sayıyor ve
kursun geri kalanının atıf yapacağı sayıyı üretiyor.

#### Bağ Nerede Kuruluyor

Bir arayüz bir yöntem kümesi tanımlar ve kendi başına hiçbir şey yapmaz — ne bir alan taşır ne
bir davranış üretir. Bir tip, o yöntem kümesinin tamamını kendi yöntemleriyle karşılıyorsa
arayüzü **karşılar**. Karşılamanın **örtük** olması, tipin kaynağında bunu bildiren hiçbir
sözcüğün geçmemesi demek. Bir tip, kendi yazarının hiç duymadığı bir arayüzü bile
karşılayabilir; tek koşul yöntem imzalarının eşleşmesidir.

Karşılamanın kendisi hiçbir zaman kaynakta yazılı değildir. Ama bu karşılamayı **doğrulayan
bir kanıt** kaynağa eklenebilir — o zaman bağ bildirime taşınır. Bu ayrım dersin son
bölümünün konusu. Şimdilik üç sınıfın sözlü tanımı yeterli: hangi bağın kaynakta açıkça
durduğunu, hangisinin derleyici tarafından kullanım anında kurulduğunu, hangisinin çalışma
zamanında değerle birlikte taşındığını ölçüm **saydırıyor**; bu bilgiyi biz baştan biliyoruz.

#### Çerçeve: Bağı Sınıflayan Bir Gözlemci

Ölçüm, her bağı tek bir soruyla sınıflayan küçük bir gözlemciden geçiyor. Üç paket düzeyi
sayaç (`bildirim`, `kullanimYeri`, `deger`) her gözlemi bir sınıfa yazıyor; `gozle` tek giriş
noktası, `ozet` toplamı basıyor.

```go
// bag.go — bir bag nerede kuruluyor: bildirimde mi, kullanim yerinde mi, degerin icinde mi
package main

import "fmt"

var bildirim, kullanimYeri, deger int

func gozle(ad, yer, kanit string) {
	switch yer {
	case "bildirim":
		bildirim++
	case "kullanim":
		kullanimYeri++
	default:
		deger++
	}
	fmt.Printf("%-40s %-10s %s\n", ad, yer, kanit)
}

func ozet() {
	fmt.Printf("bag: %d bildirim, %d kullanim yeri, %d deger\n", bildirim, kullanimYeri, deger)
}
```

**AR1.** `gozle` her satırı üç parçayla alıyor: bir ad, bir sınıf, bir kanıt. Kanıt sütunu iddiayı
doğrulayan gerçek değeri taşıyor — hangi satırın hangi sınıfa girdiği elde önceden biliniyor;
çerçeve bu kararı üretmiyor, yalnız **saydırıyor**. Sayaçların paket düzeyinde tutulması, `ozet`
çağrıldığında toplamın gözlemlerle her zaman tutarlı kalmasını sağlıyor.

#### On Altı Bağ

Bu çerçeve tek bir pakette on altı bağı sırayla soruyor. Ölçüm arayüz karşılamasıyla açılıyor,
gömme ve derleme zamanı kanıtla sürüyor, boş arayüze konan bir değerin dinamik tipiyle,
tip iddiasının ve tip anahtarının değere sorduğu soruyla, tüketici tarafında tanımlanan bir
arayüzle, hata değerinin örtük karşılanmasıyla, nöbetçi bir hatanın kimliğiyle, sarmalanan bir
hatanın taşıdığı bağlamla ve son olarak genel bir fonksiyonun çıkardığı tip parametresiyle,
bir tip kısıtının bildirimiyle, genel bir yapının örneklenmesiyle kapanıyor.

```go
// olcum.go — on alti bag, uc yere bolunur
package main

import (
	"errors"
	"fmt"
)

type Yazar interface{ Yaz(m string) string }
type Kapatan interface{ Kapat() error }

// Kisa, tuketici tarafinda tanimlanan bir arayuz: ureten tip onu hic anmiyor.
type Kisa interface{ Yaz(m string) string }

// Gomulu, iki sozlesmeyi bildirimde birlestiriyor.
type Gomulu interface {
	Yazar
	Kapatan
}

type Dosya struct{ Ad string }

func (d Dosya) Yaz(m string) string { return d.Ad + ":" + m }
func (d Dosya) Kapat() error        { return nil }

// Derleme zamani uyum kaniti: yazan taraf bunu bildirime koydu.
var _ Gomulu = Dosya{}

type Kayit struct{ Sayi int }

func (k *Kayit) Hata() string { return "kayit" }

var ErrYok = errors.New("bulunamadi")

type AlanHatasi struct{ Alan string }

func (a *AlanHatasi) Error() string { return "alan gecersiz: " + a.Alan }

func ara(bulundu bool) error {
	if !bulundu {
		return fmt.Errorf("arama basarisiz: %w", ErrYok)
	}
	return nil
}

func dogrula() error {
	return fmt.Errorf("kayit dogrulanamadi: %w", &AlanHatasi{Alan: "ad"})
}

func ilki[T any](d []T) (T, bool) {
	var sifir T
	if len(d) == 0 {
		return sifir, false
	}
	return d[0], true
}

type Sayisal interface{ ~int | ~float64 }

func topla[T Sayisal](d []T) T {
	var t T
	for _, e := range d {
		t += e
	}
	return t
}

type Yigin[T any] struct{ ogeler []T }

func (y *Yigin[T]) Ekle(o T) { y.ogeler = append(y.ogeler, o) }
func (y *Yigin[T]) Tip() string {
	var sifir T
	return fmt.Sprintf("%T", sifir)
}

func main() {
	fmt.Println("-- bag nerede kuruluyor --")

	var y Yazar = Dosya{Ad: "not"}
	gozle("tipin arayuzu karsilamasi", "kullanim", "kaynakta hicbir yerde yazili degil")
	gozle("derleme zamani uyum kaniti", "bildirim", "var _ Gomulu = Dosya{}")
	gozle("arayuz gomme", "bildirim", "Gomulu = Yazar + Kapatan")

	gozle("arayuz degerinin dinamik tipi", "deger", fmt.Sprintf("%T", y))

	d, tamam := y.(Kapatan)
	gozle("tip iddiasinin yaniti", "deger", fmt.Sprint(tamam, " ", d != nil))

	var tur string
	switch y.(type) {
	case Dosya:
		tur = "Dosya"
	default:
		tur = "bilinmeyen"
	}
	gozle("tip anahtarinin sectigi dal", "deger", tur)

	var _ Kisa = Dosya{}
	gozle("arayuzun tuketici tarafinda tanimlanmasi", "kullanim",
		"Dosya dort arayuzu karsiliyor, kaynaginda sifir atif var")

	var bosArayuz any = y
	gozle("bos arayuze konan degerin tipi", "deger", fmt.Sprintf("%T", bosArayuz))

	var k *Kayit
	var h any = k
	gozle("nil isaretci tasiyan arayuz", "deger", fmt.Sprint("isaretci nil=", k == nil, " arayuz nil=", h == nil))

	var hata error = &AlanHatasi{Alan: "ad"}
	gozle("hata arayuzunun karsilanmasi", "kullanim", fmt.Sprintf("%T", hata))

	sarmali := ara(false)
	gozle("nobetci hatanin kimligi", "deger", fmt.Sprint(errors.Is(sarmali, ErrYok)))
	gozle("sarmalamanin tasidigi metin", "deger", sarmali.Error())

	var hedef *AlanHatasi
	gozle("sarmali zincirden tip cikarma", "deger", fmt.Sprint(errors.As(dogrula(), &hedef), " ", hedef.Alan))

	ilk, varMi := ilki([]string{"a", "b"})
	gozle("genel fonksiyonun tip cikarimi", "kullanim", fmt.Sprint(ilk, " ", varMi))

	gozle("tip kisitinin bildirimi", "bildirim", fmt.Sprint("Sayisal ile toplam=", topla([]int{1, 2, 3})))

	var yigin Yigin[float64]
	yigin.Ekle(1.5)
	gozle("genel tipin orneklenmesi", "bildirim", "Yigin[float64] -> "+yigin.Tip())

	ozet()
}
```

```
-- bag nerede kuruluyor --
tipin arayuzu karsilamasi                kullanim   kaynakta hicbir yerde yazili degil
derleme zamani uyum kaniti               bildirim   var _ Gomulu = Dosya{}
arayuz gomme                             bildirim   Gomulu = Yazar + Kapatan
arayuz degerinin dinamik tipi            deger      main.Dosya
tip iddiasinin yaniti                    deger      true true
tip anahtarinin sectigi dal              deger      Dosya
arayuzun tuketici tarafinda tanimlanmasi kullanim   Dosya dort arayuzu karsiliyor, kaynaginda sifir atif var
bos arayuze konan degerin tipi           deger      main.Dosya
nil isaretci tasiyan arayuz              deger      isaretci nil=true arayuz nil=false
hata arayuzunun karsilanmasi             kullanim   *main.AlanHatasi
nobetci hatanin kimligi                  deger      true
sarmalamanin tasidigi metin              deger      arama basarisiz: bulunamadi
sarmali zincirden tip cikarma            deger      true ad
genel fonksiyonun tip cikarimi           kullanim   a true
tip kisitinin bildirimi                  bildirim   Sayisal ile toplam=6
genel tipin orneklenmesi                 bildirim   Yigin[float64] -> float64
bag: 4 bildirim, 4 kullanim yeri, 8 deger
```

On altı bağın **4'ü bildirimde, 4'ü kullanım yerinde, 8'i değerin içinde** kuruluyor. Bu
dağılım kursun geri kalanı için bağlayıcı; sonraki dersler kendi diliminin satırlarını basıp
bu tabloya tek cümleyle atıf yapacak. Sekizinin değerde kurulması Go'nun arayüz değerinin bir
**çift** olmasından geliyor — içinde hem somut tip hem değer duruyor; tip iddiası, tip
anahtarı, nöbetçi hata karşılaştırması ve sarmalı zincirden tip çıkarma dördü de aynı çiftin
okunması. Bu derste asıl konu ilk iki satır ve yedinci satır: arayüz karşılamasının kendisi ve
onun tüketici tarafında tanımlanması.

#### Örtük Gerçekleştirim: Sıfır Atıflı Dört Arayüz

`Dosya` tipinin kaynağı üç satırdan ibaret: bir alan, iki yöntem (`Yaz`, `Kapat`). Bu iki yöntem
satırı dört ayrı arayüzü karşılıyor — `Yazar` (`Yaz` yöntemini istiyor), `Kapatan` (`Kapat`
yöntemini istiyor), `Gomulu` (ikisini birden gömerek istiyor) ve `Kisa` (yalnız `Yaz` istiyor,
`Yazar`la aynı imzayı taşıyan ayrı bir tanım). `Dosya`nın kendi kaynağında bu dört arayüzün adı
**hiç geçmiyor** — ne `type Dosya struct` satırında, ne `Yaz` ve `Kapat` yöntemlerinin gövdesinde.
Kaynağa sıfır atıf var, karşılama sayısı dört.

Bu, örtük gerçekleştirimin somut sonucu: bir tip, kendi yazarının hiç görmediği bir arayüzü
bile karşılayabiliyor, çünkü karşılamanın koşulu yalnızca yöntem imzalarının eşleşmesi. `Kisa`
arayüzü `olcum.go` içinde `Yazar`dan **sonra** tanımlandı ve `Dosya` ondan habersiz yazıldı;
buna rağmen `var _ Kisa = Dosya{}` satırı derleniyor. Tablodaki yedinci satır bunu bir sayıya
indiriyor: dört arayüz, sıfır atıf. Bu gözlem beşinci dersin konusu olan tüketici tarafında
tanımlamanın ilk görünümü — orada aynı fikir bir tasarım kararına dönüşecek.

#### Küçük Arayüz İlkesi: Öğüt Değil Ölçüm

Bir arayüzün yöntem sayısı arttıkça, onu karşılayan tip sayısının düşmesi beklenir — daha
çok yöntem istemek daha sıkı bir koşul demek. Bu, Tasarım İlkeleri kursunun arayüz ayrımı
ilkesinin dayandığı sezgi; burada bir öğüt olarak değil, sayılabilir bir ilişki olarak
ölçülüyor.

```go
// kucuk-arayuz.go — arayuzun yontem sayisi arttikca onu karsilayan tip sayisi azaliyor mu
package main

import "fmt"

type BirYontemli interface{ Oku() string }
type IkiYontemli interface {
	Oku() string
	Yaz(m string)
}
type UcYontemli interface {
	Oku() string
	Yaz(m string)
	Kapat() error
}

type SadeceOkuyan struct{}

func (SadeceOkuyan) Oku() string { return "oku" }

type OkuyupYazan struct{}

func (OkuyupYazan) Oku() string  { return "oku" }
func (OkuyupYazan) Yaz(m string) {}

type OkuyupYazipKapatan struct{}

func (OkuyupYazipKapatan) Oku() string  { return "oku" }
func (OkuyupYazipKapatan) Yaz(m string) {}
func (OkuyupYazipKapatan) Kapat() error { return nil }

type SadeceYazan struct{}

func (SadeceYazan) Yaz(m string) {}

func main() {
	tipler := []any{SadeceOkuyan{}, OkuyupYazan{}, OkuyupYazipKapatan{}, SadeceYazan{}}

	birSayisi, ikiSayisi, ucSayisi := 0, 0, 0
	for _, t := range tipler {
		if _, tamam := t.(BirYontemli); tamam {
			birSayisi++
		}
		if _, tamam := t.(IkiYontemli); tamam {
			ikiSayisi++
		}
		if _, tamam := t.(UcYontemli); tamam {
			ucSayisi++
		}
	}
	fmt.Println("bir yontemli arayuzu karsilayan tip sayisi:", birSayisi)
	fmt.Println("iki yontemli arayuzu karsilayan tip sayisi:", ikiSayisi)
	fmt.Println("uc yontemli arayuzu karsilayan tip sayisi :", ucSayisi)
}
```

```
bir yontemli arayuzu karsilayan tip sayisi: 3
iki yontemli arayuzu karsilayan tip sayisi: 2
uc yontemli arayuzu karsilayan tip sayisi : 1
```

**AR2.** Dört tip bilinçli olarak kuruldu: üçü `Oku`yu artan yöntem kümeleriyle taşıyor,
dördüncüsü (`SadeceYazan`) hiç `Oku` taşımıyor ve üç arayüzün de dışında kalıyor. Bu son tip
olmasaydı düşüş yine görülürdü ama "arayüzü hiç karşılamayan bir tip de var" gözlemi
kaybolurdu — ölçüm bu yüzden dördü birlikte tutuyor.

Dört tip, üç arayüz karşısında sınandığında sayı **3, 2, 1** olarak düşüyor: yöntem kümesi
bir yöntemden üçe çıktıkça, aynı dört tipten onu karşılayabilenlerin sayısı azalıyor. Bu bağ
tabloda görülen "kullanım yeri" sınıfının aynısı — her sayım, bir tip iddiasının çağrıldığı
satırda kuruluyor, tipin ya da arayüzün kendi tanımında değil. Küçük arayüz, az yöntem isteyen
arayüz demek; az yöntem isteyen arayüz, çoğu tipin karşıladığı arayüz demek. Bu bir tasarım
öğüdü değil, yukarıdaki sayımın doğrudan sonucu.

#### Sınırlayıcı Ölçüm: Uyum Kaynakta da Yazılabilir

On altı bağın ilk satırı "kullanım yeri" diyordu ve kanıtı dolaylıydı: karşılamanın kaynakta
yazılı olmadığını gösterdi, kurulduğu yeri değil. Bu bölüm doğrudan kanıtı veriyor: uyumu
kasten bozup derleyicinin nerede durduğunu gösteriyor. Araç, Go Temelleri kursunun `eksik.go`
çerçevesindeki `derlenirMi` — bir kaynak parçasını geçici bir pakete yazıp derleyiciyi çağırıyor
ve yalnız geçip geçmediğini okuyor.

```go
// arac.go — bir kaynak parcasinin derlenip derlenmedigini programin icinden sinar
package main

import (
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}
```

Bu iki kaynak parçası aynı yarım gerçekleştirimi taşıyor — bir tip, iki yöntemli bir arayüzün
yalnız birini yazıyor. Fark tek satır: ikincisinde tipin hemen ardından bir uyum kanıtı var.

```go
// uyum-kaniti.go — kanit yokken derleyici sessiz kaliyor, kanit varsa bildirimde duruyor
package main

import "fmt"

func main() {
	kaynakA := `package sinama

type TamArayuz interface {
	Birinci() string
	Ikinci() string
}

type YarimGerceklestirim struct{}

func (y YarimGerceklestirim) Birinci() string { return "birinci" }
`
	kaynakB := kaynakA + `
var _ TamArayuz = YarimGerceklestirim{}
`
	fmt.Println("kanitsiz, hic kullanilmadan   :", derlenirMi(kaynakA))
	fmt.Println("kanitla, tanimin hemen ardinda:", derlenirMi(kaynakB))
}
```

```
kanitsiz, hic kullanilmadan   : true
kanitla, tanimin hemen ardinda: false
```

`YarimGerceklestirim` iki kaynakta da aynı — `Ikinci` yöntemi eksik. Birinci kaynakta bu tip
hiçbir yerde `TamArayuz` olarak kullanılmıyor; derleyici sessiz kalıyor ve eksik yöntem hiçbir
uyarı üretmiyor. İkinci kaynakta tek eklenen satır, tipin tanımının hemen ardına konan bir
uyum kanıtı; aynı eksiklik şimdi derlemeyi durduruyor. Bağ ikinci kaynakta **bildirime**
taşınmış oldu — kanıt satırı, karşılamanın kendisini değil, karşılamanın **doğrulamasını**
kaynağa yazıyor ve bu doğrulama bozulduğunda derleme orada duruyor.

**AR3.** Bu ölçümün bir kör noktası var: `derlenirMi` yalnız `cmd.Run()`ın çıkış durumunu
okuyor, hata metnini hiç okumuyor. Birinci kaynağın gerçekten **başka** bir nedenle değil,
yalnız eksik yöntem yüzünden derlendiğinden emin olmak elle kuruldu — kaynak, `Ikinci`
dışında hiçbir sorun taşımayacak şekilde yazıldı. Kursun derleyici çağıran her ölçümünde aynı
disiplin sürüyor.

Uyum kanıtı olmadan bırakılan bir tip, hiç kullanılmadığı sürece eksikliğini asla göstermez.
Bu, "kullanım yerinde kuruluyor" demenin gerçek maliyeti: bağ yalnız kullanıldığı anda
sınanıyor, kullanılmazsa hiç sınanmıyor. Bir kanıt satırı bu riski, tipin tanımının yanına
taşıyarak kapatıyor.

#### Özet

- Bir tipin bir arayüzü karşılaması örtüktür: bunu bildiren hiçbir sözcük kaynakta geçmez;
  tek koşul yöntem imzalarının eşleşmesidir.
- On altı bağın **4'ü bildirimde, 4'ü kullanım yerinde, 8'i değerin içinde** kuruluyor —
  kursun bağlayıcı dağılımı bu derste üretildi.
- `Dosya` tipi dört arayüzü (`Yazar`, `Kapatan`, `Gomulu`, `Kisa`) karşılıyor ve kendi
  kaynağında bu dördüne sıfır atıf var.
- Küçük arayüz ilkesi bir öğüt değil ölçüm: yöntem sayısı bir–iki–üçe çıktıkça, aynı dört
  tipten onu karşılayabilenlerin sayısı üç–iki–bire düşüyor.
- Derleme zamanı uyum kanıtı (`var _ Arayuz = Tip{}`) bağı bildirime taşıyor; kanıt yokken
  eksik bir gerçekleştirim, kullanılmadığı sürece hiç görünmüyor.

#### Sonraki Adım

Bu derste arayüz değerinin dinamik tipi birkaç kez göründü — `%T` ile okunan `main.Dosya` — ama
üzerinde durulmadı. Sıradaki ders tam olarak bu satırı büyütüyor: hiçbir yöntem istemeyen boş
arayüz, hangi tip konursa konsun onu kabul ediyor, ama konan tipin bilgisi kaybolmuyor. O bilgi
nereye gidiyor ve `%T` onu nasıl geri okuyor?

### Boş Arayüz ve `any`

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/bos-arayuz-ve-any

Boş arayüz sıfır yöntem istiyor ve sıfır yöntemi her tip karşılıyor: kaynakta hangi tipin geleceği hiç yazılı değil, tip bilgisi değerin içinde taşınıyor. Aynı değer üç ayrı statik tiple taşındığında dinamik tip değişmiyor; bedeli, değere konan her şeyin çift hâline gelip doğrudan çağrılamaması. Sınırlayıcı ölçüm: nil bir işaretçi boş arayüze konduğunda arayüzün kendisi nil değil.

Önceki ders arayüz değerinin dinamik tipini bir kez okudu — `%T` ile `main.Dosya` — ve
üzerinde durmadı. Boş arayüz tam olarak bu satırın kendisi: hiçbir yöntem istemeyen bir
arayüz, kayıt sayısıyla değil sıfır sayısıyla tanımlanan bir arayüz. Kaynakta hangi tipin bu
arayüze konacağı hiç yazılı olmadığı için, konan tipin bilgisini birinin bir yerde taşıması
gerekiyor; önceki dersin bağlayıcı dağılımında o yerin adı zaten geçmişti — **değer**. Bu
ders o satırı büyütüyor.

Önceki dersteki `Yazar` ve `Kapatan` gibi arayüzler birer yöntem istiyordu; `Gomulu` ikisini
birden istiyordu. Boş arayüz bu sıralamanın öbür ucunda duruyor: hiç yöntem istemiyor. Bir
arayüzün ne kadar az istediği ile onu ne kadar çok tipin karşılayabildiği arasındaki ilişki
önceki derste sayıyla ölçülmüştü; bu ders o eğrinin sıfır noktasına odaklanıyor ve orada
neyin kazanılıp neyin kaybedildiğini ölçüyor.

#### Sıfır Yöntem İsteyen Arayüz

Önceki derste yöntem sayısı bir, iki, üçe çıktıkça, aynı dört tipten onu karşılayabilenlerin
sayısı üç, iki, bire düşüyordu. Boş arayüz bu eğrinin sıfır ucu: `interface{}` hiç yöntem
istemiyor ve hiçbir yöntemi olmayan bir koşulu her tip karşılıyor — boş bir küme her kümenin alt
kümesidir. Dilin kendi tip adı `any`, `interface{}`in takma adı; bu ders arayüzün kavramına **boş
arayüz**, kod içindeki adına `any` diyor.

Boş arayüz hiçbir yöntem istemediği için hangi tipin ona konacağını kaynak hiç
sınırlamıyor: `var v any = 42`, `var v any = "metin"`, `var v any = Birim{}` üçü de
derleniyor, çünkü üçünün de karşılaması gereken sıfır yöntem var. Ama arayüzün sıfır yöntem
istemesi, konan değerin hiçbir bilgisinin kalmadığı anlamına gelmiyor. Tip bilgisi
kaybolmuyor, yalnız görünürlüğü değişiyor — kaynaktan okunabilen bir tip adı olmaktan çıkıp
çalışma zamanında sorulması gereken bir bilgiye dönüşüyor.

Bu ders boyunca yazılan her `fmt.Println` çağrısı da aslında aynı düzeneği kullanıyor:
standart kütüphanenin bu işlevi, parametrelerini `...any` olarak alıyor. Bir tam sayı, bir
dizgi ya da bir yapı — hangisi verilirse verilsin, çağrı sırasında her biri boş arayüze
konuyor ve `Println`in içinde `%T` benzeri bir sorgulamayla geri okunuyor. Boş arayüz bu
yüzden nadir görülen bir kenar durum değil; bu dersteki her çıktı satırı zaten ondan geçti.

#### Aynı Değer, Üç Statik Tip

Bir değerin dinamik tipi, o değerin hangi statik tip üzerinden taşındığından bağımsız.
Aşağıdaki ölçüm aynı `Birim` değerini üç ayrı statik tipin arkasından geçiriyor — kendi
arayüzü, sonra boş arayüz, sonra doğrudan boş arayüz — ve her seferinde `%T` ile dinamik tipi
okuyor.

```go
// ayni-deger.go — ayni deger uc ayri statik tipte tasindiginda dinamik tip degismiyor
package main

import "fmt"

type Birim struct{ Ad string }

func (b Birim) Kimlik() string { return b.Ad }

type Tanimlayici interface{ Kimlik() string }

func main() {
	b := Birim{Ad: "birim-42"}

	var t1 Tanimlayici = b
	var t2 any = t1
	var t3 any = b

	fmt.Println("Tanimlayici olarak       :", fmt.Sprintf("%T", t1))
	fmt.Println("any'ye tasindiktan sonra :", fmt.Sprintf("%T", t2))
	fmt.Println("dogrudan any olarak      :", fmt.Sprintf("%T", t3))
}
```

```
Tanimlayici olarak       : main.Birim
any'ye tasindiktan sonra : main.Birim
dogrudan any olarak      : main.Birim
```

Üç değişken üç ayrı statik tip taşıyor — `Tanimlayici`, sonra `any`, sonra yine `any` ama
başka bir yoldan gelmiş — ve üçü de aynı yanıtı veriyor: `main.Birim`. `t1`den `t2`ye geçiş
bir dönüşüm değil, aynı çiftin (tip, değer) yeniden bir değişkene bağlanması; tip bilgisi
kaybolmuyor çünkü hiçbir noktada kaybedilecek bir şey olmuyor, arayüz değeri zaten en
baştan beri bu çifti taşıyordu. `Tanimlayici`nin tek yöntemi de, boş arayüzün sıfır
yöntemi de, aynı temel yapıyı görüntülüyor; yalnız görünen yöntem sayısı farklı.

**AR4.** `t3 := b` yerine `t3 any = b` yazılmasının nedeni, `Birim`in doğrudan boş arayüze
konduğu bir yolu da göstermek — `t1` ve `t2` zincirinin `Tanimlayici` üzerinden geçtiğini,
`t3`ün geçmediğini ayırt etmek için üçüncü bir satır bilinçli olarak eklendi.

#### Aynı Kap, Dört Ayrı Dinamik Tip

Önceki ölçüm tek bir değeri üç ayrı statik tipten geçirdi ve dinamik tipin sabit kaldığını
gösterdi. Bu ölçüm tersini yapıyor: tek bir statik tip (`[]any`) içine dört ayrı dinamik tip
koyuyor ve her birinin kendi kimliğini koruduğunu gösteriyor.

```go
// karisik.go — ayni any dilimi ic ice farkli dinamik tipler tasiyor
package main

import "fmt"

type Birim struct{ Ad string }

func main() {
	degerler := []any{42, "metin", Birim{Ad: "x"}, 3.14}
	for _, d := range degerler {
		fmt.Printf("deger=%v dinamik tip=%T\n", d, d)
	}
	fmt.Println("dilimin statik tipi: []any, uzunluk:", len(degerler))
}
```

```
deger=42 dinamik tip=int
deger=metin dinamik tip=string
deger={x} dinamik tip=main.Birim
deger=3.14 dinamik tip=float64
dilimin statik tipi: []any, uzunluk: 4
```

**AR5.** Dizi bilinçli olarak dört ayrı temel kategoriden seçildi — bir tam sayı, bir dizgi,
bir yapı, bir kayan noktalı sayı — ki gösterim tek bir tip ailesine (örneğin yalnız sayısal
tipler) özgü bir davranış değil, boş arayüzün genel kuralı olduğu görülebilsin.

Dilimin kendisi tek bir statik tip taşıyor: `[]any`. Ama dört ögesi dört ayrı dinamik tip taşıyor
— `int`, `string`, `main.Birim`, `float64` — ve `range` döngüsü bunların hiçbirini birbirine
karıştırmıyor, çünkü her öge kendi çiftini kendisiyle birlikte getiriyor. Bu, önceki ölçümün
aynasıdır: orada aynı tip üç statik biçimde sabit kaldı, burada aynı statik biçim dört ayrı tipi
taşıyor. İkisi birlikte tip bilgisinin nerede durduğunu netleştiriyor — statik tipte değil, her
değerin kendi taşıdığı çiftte.

Bu tür heterojen bir kap — tek bir dilimde birbirinden farklı tipte değerler tutmak — tip
parametreleriyle yazılan genel veri yapılarından önce yalnız boş arayüzle kurulabiliyordu.
İki yazımın nerede ayrıştığı kursun genellikler bölümünde ölçülecek; burada yalnız boş
arayüzün bunu nasıl yaptığı önemli: her ögeyi kendi çiftiyle birlikte saklayarak.

#### Bedel: Çift Olmak, Doğrudan Çağrılamamak

Tip bilgisinin değerde taşınmasının bir bedeli var: boş arayüze konan bir değer, kendi
yöntemleriyle **doğrudan** çağrılamıyor. `any` sıfır yöntem istediği için sıfır yöntem
sunuyor — üzerinde tanımlı hiçbir yöntem yok, konan değerin kendi yöntemleri olsa bile.
Çağırmak için önce tip iddiasıyla asıl tipe (ya da asıl tipin karşıladığı bir arayüze)
dönmek gerekiyor.

Bu, önceki dersteki küçük arayüz ölçümünün simetrik karşılığı. Orada yöntem sayısı arttıkça
onu karşılayan tip sayısı azalıyordu; burada yöntem sayısı sıfıra indiğinde erişim de
sıfırlanıyor. Boş arayüz her tipi kabul ediyor çünkü hiçbir şey istemiyor ve hiçbir şey
istemediği için hiçbir şey de sunmuyor — kabul genişliği ile doğrudan erişim ters orantılı.
Derleyici burada bir tahmin yürütmüyor: `v`nin statik tipi `any` olduğu sürece, `v` üzerinde
hangi yöntemlerin bulunduğunu bilmesinin hiçbir yolu yok, çünkü bu bilgi ancak çalışma
zamanında değerin kendisine bakılarak öğrenilebilir.

```go
// arac.go — bir kaynak parcasinin derlenip derlenmedigini programin icinden sinar
package main

import (
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}
```

Bu blok yalnız diske yazılıyor; iki kaynak parçasını üretip derleyiciyi çağıran asıl ölçüm
aşağıda.

```go
// bedel.go — any uzerinden dogrudan cagri reddediliyor, tip iddiasindan sonraki cagri gecerli
package main

import "fmt"

func main() {
	ortak := `package sinama

type Birim struct{ Ad string }

func (b Birim) Kimlik() string { return b.Ad }

type Tanimlayici interface{ Kimlik() string }

func F() string {
	var v any = Birim{Ad: "x"}
`
	dogrudanCagri := ortak + `	return v.Kimlik()
}
`
	iddiaylaCagri := ortak + `	return v.(Tanimlayici).Kimlik()
}
`
	fmt.Println("dogrudan cagri, any uzerinden  :", derlenirMi(dogrudanCagri))
	fmt.Println("tip iddiasiyla cagri           :", derlenirMi(iddiaylaCagri))
}
```

```
dogrudan cagri, any uzerinden  : false
tip iddiasiyla cagri           : true
```

`v.Kimlik()` derlenmiyor, çünkü `v`nin statik tipi `any` ve `any`nin yöntem kümesi boş;
derleyici `Kimlik` adlı bir yöntemi `any` üzerinde aramıyor bile, çünkü orada aranacak
hiçbir yöntem tanımlı değil. `v.(Tanimlayici).Kimlik()` derleniyor, çünkü tip iddiası önce
değeri `Tanimlayici`ye çeviriyor ve `Tanimlayici`nin (dolayısıyla `Kimlik`in) yöntem kümesi
üzerinden çağrı yapılabiliyor. Bedel şu: `any` bir değeri her tipten kabul ediyor, ama
karşılığında o değerin kendi davranışına doğrudan erişimi kapatıyor. Tip iddiasının kendisi
sıradaki dersin konusu; burada yalnız onun **gerekliliği** ölçülüyor.

Bu ret bir eksiklik değil, bilinçli bir sınır. Derleyici `v.Kimlik()` satırını görürken elinde
yalnız `v`nin statik tipi (`any`) var; hangi somut değerin orada duracağı derleme zamanında
henüz bilinmiyor, ilk çalıştığında belirleniyor. Derleyici bu boşluğu "muhtemelen `Kimlik`
vardır" diye tahmin ederek doldurmuyor — böyle bir tahmin, yanlış çıktığında ancak çalışma
zamanında, belki de üretim ortamında patlardı. Bunun yerine derleyici, statik tipin sunduğu
yöntem kümesinin dışına hiç çıkmıyor; boş arayüz için bu küme boş olduğundan, çağrı orada
duruyor. Tip iddiası bu boşluğu **açıkça** kapatan tek yol.

#### Sınırlayıcı Ölçüm: Nil İşaretçi Taşıyan Arayüz Nil Değildir

Boş arayüzün en tuhaf satırı, tip bilgisinin bedelsiz taşınmadığını gösteriyor. Bir işaretçi
`nil` olabilir; aynı işaretçi boş arayüze konduğunda, arayüzün kendisi **nil olmuyor**.

```go
// nil-arayuz.go — isaretci nil olsa da onu tasiyan arayuz nil degil
package main

import "fmt"

type Sunucu struct{}

func main() {
	var s *Sunucu
	var v1 any = s
	var v2 any

	fmt.Println("isaretcinin kendisi nil mi  :", s == nil)
	fmt.Println("isaretciyi tasiyan v1 nil mi:", v1 == nil)
	fmt.Println("hic deger konmayan v2 nil mi:", v2 == nil)
}
```

```
isaretcinin kendisi nil mi  : true
isaretciyi tasiyan v1 nil mi: false
hic deger konmayan v2 nil mi: true
```

Üç satır bilinçli olarak yan yana konuldu. `s` gerçekten `nil` — hiçbir `Sunucu` örneğini
göstermiyor. `v1` bu `nil` işaretçiyi taşıyor, ama `v1 == nil` `false` veriyor: arayüz
değeri bir çift ve bu çiftin tip yuvası dolu — `*main.Sunucu` orada duruyor — yalnız değer
yuvası `nil`. Çift yalnız **her iki yuvası da** boşken `nil` sayılıyor; `v2` hiç değer
almadığı için hem tip hem değer yuvası boş kalıyor ve gerçekten `nil`.

Çiftin tip yuvasının gerçekten ayrı bir bilgi taşıdığı, iki farklı işaretçi tipi yan yana
konduğunda daha da netleşiyor.

```go
// iki-nil.go — iki ayri isaretci tipinin nil degerleri birbirine esit sayilmiyor
package main

import "fmt"

type A struct{}
type B struct{}

func main() {
	var a *A
	var b *B
	var v1 any = a
	var v2 any = b

	fmt.Println("v1 nil mi       :", v1 == nil)
	fmt.Println("v2 nil mi       :", v2 == nil)
	fmt.Println("v1 v2'ye esit mi:", v1 == v2)
	fmt.Printf("v1 dinamik tip=%T, v2 dinamik tip=%T\n", v1, v2)
}
```

```
v1 nil mi       : false
v2 nil mi       : false
v1 v2'ye esit mi: false
v1 dinamik tip=*main.A, v2 dinamik tip=*main.B
```

`a` ve `b` ikisi de `nil` işaretçi ve ikisi de sıfır alanlı, görünüşte "boş" birer yapıyı
gösteriyor. Ama `v1` ile `v2` birbirine eşit değil, çünkü arayüz karşılaştırması önce tip
yuvasını karşılaştırıyor ve `*main.A` ile `*main.B` aynı tip değil. İki değer yuvası da
`nil` olsa bile, tip yuvaları farklı olduğu için çift olarak eşit sayılmıyorlar. Bu, tip
yuvasının değer yuvasından bağımsız, gerçek bir bilgi taşıdığının bir başka kanıtı — yalnız
"dolu mu boş mu" değil, "hangi tip" sorusunun da karşılaştırmaya girdiğini gösteriyor.

**AR6.** Bu, önceki dersin birinci iddiasının doğrudan sonucu: bağların sekizi değerin
içinde taşınıyorsa, değerin boş olması çiftin boş olması demek değil. Go Temelleri kursunun
Sıfır Değerler dersi `nil` dilime eklenebilirken `nil` sözlüğe yazılamadığını ölçmüştü; bu
satır aynı ailenin üçüncü örneği — `nil` sözcüğü aynı kalıyor, taşıdığı anlam tipten tipe
değişiyor.

Bu satırın pratik sonucu şu: bir fonksiyon `error` (kendisi bir arayüz) döndürüyorsa ve
içeride somut bir işaretçi tipi kullanıyorsa, o işaretçi `nil` olsa bile döndürülen hata
değeri `nil` **olmayabilir**. Çağıran taraf `hata == nil` diye sorduğunda `false` yanıtını
alır, çünkü sorduğu şey işaretçinin kendisi değil, onu taşıyan çifttir. Bu tuzağın tam
ölçümü hata değerlerine ayrılan derste yapılacak; burada yalnız kökeni gösterildi.

Kökeni bu kadar erken görmenin bir nedeni var: hata, kendisi de bir arayüz ve arayüz olan
her şey aynı çift kuralına tabi. `error` boş arayüz değil — `Error() string` yöntemini
istiyor — ama boş arayüzle paylaştığı şey tam olarak bu ölçümün konusu: ikisi de bir tip
yuvası ve bir değer yuvası taşıyan bir çift. Boş arayüzde bu çifti doğrudan gözlemlemek
kolay, çünkü hiçbir yöntem araya girmiyor; hata arayüzünde aynı çift bir yöntem imzasının
arkasına gizleniyor ama davranışı değişmiyor.

#### Özet

- Boş arayüz sıfır yöntem istiyor ve sıfır yöntemi her tip karşılıyor; `any` bu arayüzün
  kod içindeki adı.
- Aynı değer üç ayrı statik tiple (özel arayüz, ondan `any`ye, doğrudan `any`) taşındığında
  dinamik tip değişmiyor — tip bilgisi kaybolmuyor, yalnız görünürlüğü değişiyor.
- Boş arayüze konan bir değer doğrudan çağrılamıyor; `any`nin yöntem kümesi boş olduğu için
  çağrı önce bir tip iddiası gerektiriyor.
- Nil bir işaretçi boş arayüze konduğunda arayüzün kendisi nil değil — çift yalnız tip ve
  değer yuvası birlikte boşken nil sayılıyor.
- İki ayrı işaretçi tipinin `nil` değerleri boş arayüze konduğunda birbirine eşit sayılmıyor;
  karşılaştırma değer yuvasından önce tip yuvasına bakıyor.

#### Sonraki Adım

Bu derste tip bilgisine yalnız `%T` ile, salt okuma amacıyla erişildi — hiçbir satır
`v`nin gerçek değerini geri almadı, yalnız hangi tipte olduğunu okudu. Ama bir programın asıl
sorması gereken soru genellikle "bu değerin tipi ne" değil, "bu değer şu tipi karşılıyor mu"
ve karşılıyorsa "asıl değeri bana ver". Boş arayüzden çıkışta zaten bir kez görüldü:
`v.(Tanimlayici)` değeri geri kazandırdı ama yanıtı denetlemeden. Sıradaki ders bu iki
soruyu ayrı ayrı ölçüyor: tip iddiasının iki yanıtlı biçimi ve tip anahtarının seçtiği dal,
değere sormanın iki ayrı yolu.

### Tip İddiası ve Tip Anahtarı

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/tip-iddiasi-ve-tip-anahtari

Beş değerden ikisi bir arayüzü karşılıyor; tip iddiasının iki yanıtlı biçimi bunu panik olmadan sayıyor, tek yanıtlı biçim başarısız olduğunda panikliyor ve panik yalnız panik=true olarak okunuyor. Tip anahtarı aynı beş değeri kendi dallarına dağıtıyor. Sınırlayıcı ölçüm: aynı değer hem bir somut tip dalına hem bir arayüz dalına uyabiliyor ve seçilen dal yazılış sırasına bağlı — ders bunu iki sıralamayla koşturuyor.

Önceki ders boş arayüzün bir değerin tip bilgisini kaybetmeden taşıdığını ölçtü, ama o
bilgiye yalnız `%T` ile, salt okuma amacıyla baktı. Bu bilginin gerçek kullanımı okumak değil
sormaktır: bu değer şu arayüzü karşılıyor mu ve karşılıyorsa asıl değeri bana ver. Go bu
soruyu iki ayrı yapıyla soruyor — **tip iddiası** tek bir aday hakkında soruyor, **tip
anahtarı** birden çok aday arasından seçim yapıyor. İkisi de değere soruyor, ikisi de aynı
çiftin (tip, değer) içine bakıyor.

İkisinin de sorduğu şey, ilk dersteki on altı bağın en sık tekrarlanan kalıbı: bir bağ
kaynakta yazılı değilse ve derleyici onu kullanım anında kurmuyorsa, geriye tek yol
kalıyor — çalışma zamanında değerin kendisine sormak. Bu ders o sormanın iki biçimini,
sonuçlarını ve bir tanesinin taşıdığı riski ölçüyor.

#### Tip İddiasının İki Yanıtlı Biçimi

Bir tip iddiası `deger.(Tip)` biçiminde yazılıyor ve iki biçimi var. Tek yanıtlı biçim
(`v := deger.(Tip)`) yalnız değeri döndürüyor; iddia yanlışsa çalışma zamanı hatası veriyor.
İki yanıtlı biçim (`v, tamam := deger.(Tip)`) ikinci bir mantıksal değer döndürüyor;
iddia yanlışsa `tamam` `false` oluyor ve program durmuyor.

`Tip` yerine yazılan şey iki ayrı soru sorabiliyor. Somut bir tip yazıldığında ("bu değerin
dinamik tipi tam olarak bu mu") karşılaştırma dinamik tipin kendisine bakıyor. Bir arayüz
yazıldığında ("bu değer bu sözleşmeyi karşılıyor mu") karşılaştırma yöntem kümesine bakıyor.
İkisi de aynı `deger.(Tip)` yazımını paylaşıyor; hangi soru sorulduğu yalnız `Tip`in kendi
doğasından, somut mu arayüz mü olduğundan, anlaşılıyor. Aşağıdaki ölçüm bir arayüz sorusu
soruyor: beş ayrı değeri aynı arayüze karşı sınıyor ve kaçının karşıladığını sayıyor.

```go
// bes-soru.go — bes degerden kaci Depolayici arayuzunu karsiliyor
package main

import "fmt"

type Depolayici interface{ Depola() string }

type DiskKaydi struct{}

func (DiskKaydi) Depola() string { return "disk" }

type BellekKaydi struct{}

func (BellekKaydi) Depola() string { return "bellek" }

type Gecici struct{}

func main() {
	degerler := []any{DiskKaydi{}, 42, BellekKaydi{}, "metin", Gecici{}}
	dogruSayisi := 0
	for i, d := range degerler {
		depo, tamam := d.(Depolayici)
		if tamam {
			fmt.Printf("deger#%d dinamik_tip=%T Depolayici_mi=%t cagri=%s\n", i, d, tamam, depo.Depola())
			dogruSayisi++
			continue
		}
		fmt.Printf("deger#%d dinamik_tip=%T Depolayici_mi=%t\n", i, d, tamam)
	}
	fmt.Println("bes sorudan dogru sayisi:", dogruSayisi)
}
```

```
deger#0 dinamik_tip=main.DiskKaydi Depolayici_mi=true cagri=disk
deger#1 dinamik_tip=int Depolayici_mi=false
deger#2 dinamik_tip=main.BellekKaydi Depolayici_mi=true cagri=bellek
deger#3 dinamik_tip=string Depolayici_mi=false
deger#4 dinamik_tip=main.Gecici Depolayici_mi=false
bes sorudan dogru sayisi: 2
```

Beş değerden ikisi (`DiskKaydi`, `BellekKaydi`) `Depolayici`yi karşılıyor, üçü (`int`,
`string`, `Gecici`) karşılamıyor. `Gecici` bir yapı olduğu hâlde karşılamıyor, çünkü `Depola`
yöntemini hiç yazmamış — arayüz karşılamasının koşulu yapı olmak değil, yöntem kümesinin
eşleşmesi. `tamam` değişkeni bu beş sorunun her birine kesin bir yanıt veriyor ve döngü hiçbir
noktada durmuyor; bu, iki yanıtlı biçimin asıl gücü.

`depo` değişkeni bu ölçümün ikinci yarısını taşıyor. Tip iddiası yalnız "karşılıyor mu"
sorusuna yanıt vermiyor; `tamam` doğruysa `depo` artık statik tipi `Depolayici` olan,
`Depola` yöntemi çağrılabilir gerçek bir değer. `cagri=disk` ve `cagri=bellek` sütunları bu
değerin gerçekten kullanılabilir olduğunu gösteriyor — önceki dersteki "doğrudan
çağrılamaz" bedeli burada tam olarak geri ödeniyor: tip iddiası başarılı olduğunda değer,
boş arayüzün kapattığı erişimi yeniden açıyor.

**AR7.** Beş değer bilinçli olarak üç farklı başarısızlık nedeninden seçildi: `int` ve
`string` hiç yöntem taşımıyor, `Gecici` bir yapı ama yanlış (boş) yöntem kümesine sahip.
Üçü de aynı `false` yanıtını veriyor ama nedenleri farklı — ölçüm bu farkı ayırt etmiyor,
yalnız karşılayıp karşılamadığını sayıyor.

İki yanıtlı biçim başarısız olduğunda ilk değişken (`depo`) rastgele bir şey değil, hedef
tipin kendi sıfır değeri oluyor — burada `Depolayici` bir arayüz olduğu için sıfır değeri
`nil`. Bu, önceki iki dersin bağladığı bir noktayı bir kez daha doğruluyor: Go'da yazılmayan
ya da başarısız olan bir şeyin karşılığı asla tanımsız kalmıyor, her zaman kendi tipinin
sıfır değeri devreye giriyor.

#### Tek Yanıtlı Biçim Başarısız Olduğunda: Panik

İki yanıtlı biçim başarısızlığı bir değere çeviriyor; tek yanıtlı biçim aynı başarısızlığı
bir panige çeviriyor. Aşağıdaki ölçüm bunu doğrudan koşturuyor.

```go
// panik-olcumu.go — tek yanitli tip iddiasi basarisiz oldugunda panikliyor
package main

import "fmt"

type Depolayici interface{ Depola() string }

type DiskKaydi struct{}

func (DiskKaydi) Depola() string { return "disk" }

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func main() {
	var basarili any = DiskKaydi{}
	var basarisiz any = 42

	fmt.Println("basarili tek yanitli panikliyor mu :", panikliyorMu(func() {
		_ = basarili.(Depolayici)
	}))
	fmt.Println("basarisiz tek yanitli panikliyor mu :", panikliyorMu(func() {
		_ = basarisiz.(Depolayici)
	}))
	fmt.Println("basarisiz iki yanitli panikliyor mu :", panikliyorMu(func() {
		_, tamam := basarisiz.(Depolayici)
		_ = tamam
	}))
}
```

```
basarili tek yanitli panikliyor mu : false
basarisiz tek yanitli panikliyor mu : true
basarisiz iki yanitli panikliyor mu : false
```

Üç satır aynı `basarisiz` değeri (bir `int`) sınıyor, yalnız çağrı biçimi değişiyor. Tek
yanıtlı biçim `DiskKaydi{}` için panik üretmiyor, çünkü iddia gerçekten doğru. Aynı biçim
`42` için panikliyor, çünkü `int` `Depolayici`yi karşılamıyor ve tek yanıtlı biçim bu
durumda çalışma zamanı hatası fırlatıyor — `panikliyorMu` bu hatayı `recover` ile yakalayıp
yalnız `panik=true` olarak okuyor, yığıt izi hiç basılmıyor. Üçüncü satır aynı başarısız
iddiayı iki yanıtlı biçimle deniyor ve panik hiç oluşmuyor; `tamam` sessizce `false` oluyor.
Fark biçimde: aynı soru, iki ayrı sonuç sözleşmesi.

**AR8.** `panikliyorMu` burada Go Temelleri kursunun `eksik.go` çerçevesinden aynı işlevle
yeniden kuruldu: bir işlevi çalıştırıp panikleyip paniklemediğini `recover` ile yakalıyor ve
yalnız iki değerden birini döndürüyor. Bu, kursun panik ölçen her yerinde geçerli tek yöntem.

Bu iki değerlik örnek tek başına yeterli değil: beş değerin tamamı tek yanıtlı biçimden
geçirilirse, panikleyenlerin sayısı önceki bölümdeki `false` sayısıyla aynı çıkmalı — ikisi
aynı olguyu iki ayrı sözleşmeden okuyor.

```go
// panik-sayimi.go — bes degerin kaci tek yanitli bicimde panikliyor
package main

import "fmt"

type Depolayici interface{ Depola() string }

type DiskKaydi struct{}

func (DiskKaydi) Depola() string { return "disk" }

type BellekKaydi struct{}

func (BellekKaydi) Depola() string { return "bellek" }

type Gecici struct{}

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func main() {
	degerler := []any{DiskKaydi{}, 42, BellekKaydi{}, "metin", Gecici{}}
	panikSayisi := 0
	for i, d := range degerler {
		panikledi := panikliyorMu(func() {
			_ = d.(Depolayici)
		})
		fmt.Printf("deger#%d dinamik_tip=%T panikledi=%t\n", i, d, panikledi)
		if panikledi {
			panikSayisi++
		}
	}
	fmt.Println("bes degerden panikleyen sayisi:", panikSayisi)
}
```

```
deger#0 dinamik_tip=main.DiskKaydi panikledi=false
deger#1 dinamik_tip=int panikledi=true
deger#2 dinamik_tip=main.BellekKaydi panikledi=false
deger#3 dinamik_tip=string panikledi=true
deger#4 dinamik_tip=main.Gecici panikledi=true
bes degerden panikleyen sayisi: 3
```

Panikleyen sayısı üç, ilk ölçümdeki `dogruSayisi`nin (iki) tam tamamlayıcısı — beş değerin
ikisi karşılıyordu, üçü karşılamıyordu ve şimdi o üçü tek yanıtlı biçimde panikliyor. İki
ölçüm aynı beş değeri, aynı arayüze karşı, iki ayrı çağrı biçimiyle soruyor ve toplam beşe
tamamlanan iki sayı üretiyor. Bu, iki biçimin aynı bilgiyi taşıdığının bir kanıtı: fark
bilgide değil, başarısızlığın nasıl bildirildiğinde.

#### Tip Anahtarının Seçtiği Dal

Tip anahtarı, birden fazla aday arasından tek bir dal seçiyor. `switch v.(type)` yazımı her
`case`i sırayla deniyor ve eşleşen ilk daldan devam ediyor. Go Temelleri kursunun Denetim
Akışı dersinde ölçülen kendiliğinden düşmeme kuralı burada da geçerli: eşleşen dal bir
sonraki `case`e düşmüyor, `fallthrough` yazılmadığı sürece dal kendi başına bitiyor. Aynı
beş değer bu kez dört dallı bir tip anahtarından geçiyor.

```go
// dal-sec.go — tip anahtari bes degeri kendi dallarina dagitiyor
package main

import "fmt"

type Depolayici interface{ Depola() string }

type DiskKaydi struct{}

func (DiskKaydi) Depola() string { return "disk" }

type BellekKaydi struct{}

func (BellekKaydi) Depola() string { return "bellek" }

type Gecici struct{}

func dalSec(v any) string {
	switch t := v.(type) {
	case int:
		return fmt.Sprintf("tam sayi dali: %d", t)
	case string:
		return fmt.Sprintf("dizgi dali: %q", t)
	case DiskKaydi:
		return "DiskKaydi dali"
	case BellekKaydi:
		return "BellekKaydi dali"
	default:
		return "varsayilan dal"
	}
}

func main() {
	degerler := []any{DiskKaydi{}, 42, BellekKaydi{}, "metin", Gecici{}}
	for i, d := range degerler {
		fmt.Printf("deger#%d -> %s\n", i, dalSec(d))
	}
}
```

```
deger#0 -> DiskKaydi dali
deger#1 -> tam sayi dali: 42
deger#2 -> BellekKaydi dali
deger#3 -> dizgi dali: "metin"
deger#4 -> varsayilan dal
```

Beş değerin dördü kendi somut tipine ayrılmış bir dala düşüyor; `Gecici` hiçbir `case`e
uymadığı için `default`e düşüyor. `t := v.(type)` yazımındaki `t`, her dalın içinde o dala
özgü statik tipte bir değişken — `case int` dalında `t` bir `int`, `case string` dalında bir
`string`. Bu, tip anahtarının tip iddiasından farkı: tip iddiası tek bir tipi soruyor, tip
anahtarı aynı sorguyu birden çok adaya karşı tek seferde yürütüp hangisinin eşleştiğini
söylüyor.

Aynı sonucu beş ayrı tip iddiasıyla da elde etmek mümkündü — her aday için bir `if d, tamam
:= v.(X); tamam` satırı yazıp ilk doğru olanı seçmek. Tip anahtarı bunu tek bir yapıda
topluyor ve `t`nin dala özgü statik tipini otomatik olarak sağlıyor; beş ayrı iddiada bu
tipi her dalda elle yeniden yazmak gerekirdi. Kazanılan şey bir yeni yetenek değil, aynı
sorgunun tek bir yerde toplanması.

#### Sınırlayıcı Ölçüm: Seçilen Dal Yazılış Sırasına Bağlı

Tip anahtarı yalnız somut tipleri değil, arayüzleri de dal olarak kabul ediyor. Bir değer
hem bir somut tip dalına hem bir arayüz dalına uyabiliyorsa, hangisinin seçileceği dalların
**yazılış sırasına** bağlı — ilk eşleşen dal kazanıyor.

Önceki bölümdeki dört dal (`int`, `string`, `DiskKaydi`, `BellekKaydi`) birbirini dışlıyordu:
tek bir değer bu dallardan yalnız birine uyabiliyordu. Ama bir somut tip ile o tipin
karşıladığı bir arayüz birbirini dışlamıyor — `DiskKaydi` hem `DiskKaydi`nin kendisi hem
`Depolayici` demek. İki `case` aynı değeri talep ettiğinde tip anahtarı bir çakışma hatası
vermiyor, sessizce ilk yazılanı seçiyor.

```go
// sira-bagimliligi.go — ayni deger, dallarin sirasina gore iki ayri sonuc veriyor
package main

import "fmt"

type Depolayici interface{ Depola() string }

type DiskKaydi struct{}

func (DiskKaydi) Depola() string { return "disk" }

func siniflaSomutOnce(v any) string {
	switch v.(type) {
	case DiskKaydi:
		return "somut: DiskKaydi"
	case Depolayici:
		return "arayuz: Depolayici"
	default:
		return "bilinmeyen"
	}
}

func siniflaArayuzOnce(v any) string {
	switch v.(type) {
	case Depolayici:
		return "arayuz: Depolayici"
	case DiskKaydi:
		return "somut: DiskKaydi"
	default:
		return "bilinmeyen"
	}
}

func main() {
	v := DiskKaydi{}
	fmt.Println("somut dal once yazilirsa  :", siniflaSomutOnce(v))
	fmt.Println("arayuz dali once yazilirsa:", siniflaArayuzOnce(v))
}
```

```
somut dal once yazilirsa  : somut: DiskKaydi
arayuz dali once yazilirsa: arayuz: Depolayici
```

`DiskKaydi{}` iki dala da uyuyor: kendi somut tipine ve `Depolayici`ye, çünkü `Depola` yöntemini
karşılıyor. `siniflaSomutOnce` `DiskKaydi` dalını önce yazdığı için o dalı seçiyor;
`siniflaArayuzOnce` aynı değeri, yalnız dalların sırası tersine çevrildiği için, `Depolayici`
dalına düşürüyor. Kaynak metin iki işlevde de aynı iki `case`i taşıyor, yalnız sırası değişmiş;
sonuç bambaşka. Bu, kursun ilk dersindeki küçük arayüz ölçümüyle aynı gerçeğe farklı bir açıdan
bakıyor: bir değer birden çok arayüzü aynı anda karşılayabiliyor ve tip anahtarı bu çokluğu ilk
yazılan dalı seçerek çözüyor.

**AR9.** Bu sıra bağımlılığı yalnız somut tip ile arayüz dalı arasında değil, iki ayrı
arayüz dalı arasında da geçerli — bir değer iki arayüzü birden karşılıyorsa, hangi arayüz
dalının önce yazıldığı yine kazanıyor. Ölçüm burada tek bir çifti (somut tip, arayüz)
sınıyor çünkü bu, kaynakta en sık karşılaşılan biçim; ilke ikiden fazla dal için de aynı
kalıyor.

Bunun sonucu, normal bir `switch`teki alışılmış sezgiyle çelişiyor: sıradan bir `switch`te
dalların sırası genellikle yalnız okunabilirliği etkiler, sonucu değiştirmez, çünkü her dal
birbirini dışlayan ayrı bir değeri karşılaştırır. Tip anahtarında dallar birbirini
dışlamayabilir — bir değer aynı anda birden fazla dala uyabilir — ve bu durumda sıra artık
salt bir okunabilirlik tercihi değil, hangi dalın çalışacağını belirleyen bir karardır.
Ölçümün gösterdiği şey tam olarak bu: iki işlev aynı iki `case`i taşıyor, aynı değeri
alıyor ve yalnız bir satırın yerinin değişmesiyle iki ayrı yanıt üretiyor.

#### Özet

- Tip iddiasının iki yanıtlı biçimi (`v, tamam := deger.(Tip)`) başarısızlığı bir mantıksal
  değere çeviriyor; beş değerden ikisi `Depolayici`yi karşılıyor, üçü karşılamıyor.
- Tek yanıtlı biçim (`v := deger.(Tip)`) başarısız olduğunda panikliyor; panik yalnız
  `recover` ile yakalanıp `panik=true`/`panik=false` olarak okunuyor.
- Tip anahtarı aynı sorguyu birden çok adaya karşı tek seferde yürütüyor ve eşleşen ilk
  daldan devam ediyor, bir sonraki dala düşmüyor.
- Bir değer hem bir somut tip dalına hem bir arayüz dalına uyabiliyor; seçilen dal kaynağın
  yazılış sırasına bağlı, değerin kendisine değil.
- İki yanıtlı biçimin panik üretmemesi ile tek yanıtlı biçimin panik sayısının (üç) iki
  yanıtlı biçimin `false` sayısını (üç) tam karşılaması, iki çağrı biçiminin aynı bilgiyi
  taşıdığını, yalnız başarısızlığı farklı bildirdiğini gösteriyor.
- Tip iddiası başarılı olduğunda geri dönen değer gerçekten çağrılabilir; önceki dersteki
  "doğrudan çağrılamaz" bedeli, başarılı bir iddiadan sonra geri ödeniyor.

#### Sonraki Adım

Bu derste `Depolayici` tek bir yöntem istiyordu ve tek bir sözleşme kuruyordu. Ama bir tip
birden çok sözleşmeyi aynı anda karşılamak zorunda kaldığında — hem depolayabilmeli hem
kapatılabilmeli — bu iki sözleşmeyi kaynakta birleştirmenin bir yolu gerekiyor. Sınırlayıcı
ölçümdeki `Depolayici` dalı da böyle bir birleşmenin habercisiydi: `DiskKaydi` yalnız
`Depola` yazdı ama başka sözleşmeleri de karşılayabilirdi. Sıradaki ders bu birleşmeyi
ölçüyor: gömülü arayüzler bir sözleşmeyi kaynakta nasıl kuruyor ve bu birleşme
gerçekleştirime hiç yeni yöntem eklemeden nasıl oluyor?

### Arayüz Gömme

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/arayuz-gomme

Gömme, iki arayüzü kaynakta tek bir bileşik sözleşmede birleştiriyor ve bu birleşim hiçbir yeni yöntem eklemiyor: gömme ile kurulan ve elle yeniden yazılan aynı sözleşme, aynı iki yöntemi istiyor. Bileşik arayüzü karşılamak için bir tipin yazması gereken yöntem sayısı, gömülen arayüzlerin toplam yöntem sayısına eşit, ne fazla ne eksik. Sınırlayıcı ölçüm: gömme yalnız sözleşmeyi birleştiriyor, gerçekleştirimi birleştirmiyor — iki ayrı tip iki ayrı sözleşmeyi ayrı ayrı karşılayabiliyor ama bileşik sözleşmeyi hiçbiri karşılamıyor.

Önceki ders bir tipin birden çok sözleşmeyi aynı anda karşılayabildiğini gördü, ama her
sözleşme kaynakta ayrı ayrı duruyordu. Bir tip hem depolayabilmeli hem kapatılabilmeli
gerektiğinde, bu iki isteği tek bir adla anmanın bir yolu gerekiyor. Gömme bu yolu
sağlıyor: bir arayüz, başka arayüzleri kendi gövdesine yalnızca adlarını yazarak alabiliyor.
Bu, önceki üç dersteki bağların hiçbirine benzemiyor — kaynakta açıkça yazılı, dolayısıyla
**bildirim** sınıfının en açık örneği.

İlk dersteki on altı bağın yalnız dördü bildirimdeydi ve ikisi zaten gömmeyle ilgiliydi:
`Gomulu`nun kendisi ve onu karşıladığını doğrulayan derleme zamanı kanıtı. Bu ders o iki
satırı büyütüp gömmenin kendi kurallarını ölçüyor — ne birleştirdiğini, ne kadarını
birleştirdiğini ve nerede durduğunu. Üç ölçüm bu sırayla ilerliyor: önce gömmenin gerçekten
yeni bir yöntem eklemediği doğrulanıyor, sonra bileşik sözleşmeyi karşılamanın en az kaç
yöntem gerektirdiği bulunuyor, son olarak bu birleşmenin nerede durduğu — sözleşmede mi,
gerçekleştirimde mi — sınanıyor.

#### Sözleşmeleri Kaynakta Birleştirmek

Bir arayüz gömmesi, bir arayüz gövdesinin içine başka bir arayüzün adını yazmaktan ibaret.
Sonuç, gömülen arayüzlerin yöntemlerinin tamamını isteyen yeni bir arayüz. Yazım kısa:
gömülen arayüzün önüne bir alan adı konmuyor, yalnız kendi adı bir satır olarak yazılıyor —
bu, gömmenin yapı gömmesinden ödünç aldığı aynı yazım kuralı, adlandırılmamış bir alanmış
gibi görünen ama bir tip adı olan bir satır. Aşağıdaki ölçüm aynı sözleşmeyi iki yoldan
kuruyor — biri gömerek, biri elle yeniden yazarak — ve ikisinin de istediği yöntem sayısını
`reflect` ile sayıyor.

```go
// esdegerlik.go — gomme ile kurulan sozlesme, elle yazilanla ayni yontem sayisini istiyor
package main

import (
	"fmt"
	"reflect"
)

type Okuyucu interface{ Oku() string }
type Yazici interface{ Yaz(m string) }

// Gomme: iki arayuz adi, gomulen arayuzun govdesine yaziliyor.
type OkuyupYazanGomulu interface {
	Okuyucu
	Yazici
}

// Ayni sozlesme, elle yeniden yazilmis.
type OkuyupYazanElle interface {
	Oku() string
	Yaz(m string)
}

func main() {
	a := reflect.TypeOf((*OkuyupYazanGomulu)(nil)).Elem()
	b := reflect.TypeOf((*OkuyupYazanElle)(nil)).Elem()

	fmt.Println("gomme ile istenen yontem sayisi   :", a.NumMethod())
	fmt.Println("elle yazilan istenen yontem sayisi:", b.NumMethod())
	for i := 0; i < a.NumMethod(); i++ {
		fmt.Println("  yontem:", a.Method(i).Name)
	}
}
```

```
gomme ile istenen yontem sayisi   : 2
elle yazilan istenen yontem sayisi: 2
  yontem: Oku
  yontem: Yaz
```

`OkuyupYazanGomulu` kaynakta yalnızca iki adı (`Okuyucu`, `Yazici`) taşıyor, hiçbir yöntem
imzası yazmıyor. Ama `reflect` ona sorulduğunda, tıpkı iki yöntemi elle yazan
`OkuyupYazanElle` gibi, tam iki yöntem istediğini söylüyor: `Oku` ve `Yaz`. Gömme bir
kısaltma — kaynakta daha az yazmayı sağlıyor — ama ürettiği sözleşme, elle yazılanla
**birebir aynı**. Bu, Go Temelleri kursunun Yapılar dersindeki gömme kavramının bir
uzantısı: orada bir yapı başka bir yapının alan ve yöntem adlarını yazılmadan taşıyordu,
burada bir arayüz başka bir arayüzün yöntem adlarını yazılmadan taşıyor. İkisi de aynı
sözcükle (**gömme**) anılıyor çünkü ikisi de aynı düzeneği kullanıyor: bir adı yazmak,
o adın taşıdığı her şeyi getiriyor.

**AR10.** `reflect.TypeOf((*T)(nil)).Elem()` yazımı, bir arayüz tipinin kendisini (bir arayüz
*değerini* değil) `reflect.Type` olarak elde etmenin standart yolu — `(*T)(nil)` bir işaretçi
değeri üretiyor, `.Elem()` bu işaretçinin gösterdiği arayüz tipine iniyor. Bu ölçüm boyunca
hiçbir gerçek `T` değeri oluşturulmuyor, yalnız tipin kendisi sorgulanıyor.

Gömme burada iki arayüzle sınırlı değil; gömülen bir arayüz kendisi de başka bir arayüz
gömebiliyor ve zincir birkaç kademe uzayabiliyor. Aşağıdaki ölçüm `OkuyupYazanGomulu`yu
üçüncü bir arayüzle (`Kapatan`) daha gömerek bir kademe derinleştiriyor ve toplam yöntem
sayısının hâlâ doğru toplandığını sınıyor.

```go
// zincir.go — iki kademeli gomme, toplam yontem sayisini yine dogru topluyor
package main

import (
	"fmt"
	"reflect"
)

type Okuyucu interface{ Oku() string }
type Yazici interface{ Yaz(m string) }
type Kapatan interface{ Kapat() error }

type OkuyupYazan interface {
	Okuyucu
	Yazici
}

type TamKaynak interface {
	OkuyupYazan
	Kapatan
}

func main() {
	t := reflect.TypeOf((*TamKaynak)(nil)).Elem()
	fmt.Println("iki kademeli gomme, toplam yontem sayisi:", t.NumMethod())
	for i := 0; i < t.NumMethod(); i++ {
		fmt.Println("  yontem:", t.Method(i).Name)
	}
}
```

```
iki kademeli gomme, toplam yontem sayisi: 3
  yontem: Kapat
  yontem: Oku
  yontem: Yaz
```

`TamKaynak`, `OkuyupYazan`ı gömüyor, `OkuyupYazan` da kendi içinde `Okuyucu` ile `Yazici`yi
gömüyor — iki kademe. Toplam yöntem sayısı yine üç, tek tek gömülen üç yöntemin
(`Oku`, `Yaz`, `Kapat`) toplamı. Kademe sayısı arttıkça toplama kuralı değişmiyor: her
kademe kendi altındaki bütün adları yukarı taşıyor, hiçbir ad kaybolmuyor ve hiçbir ad
iki kez sayılmıyor.

Bu, `TamKaynak`ı okuyan birinin görmesi gereken şeyi netleştiriyor: kaynakta yalnız iki satır
(`OkuyupYazan`, `Kapatan`) var, ama sözleşme üç yöntem istiyor. Ara kademeyi (`OkuyupYazan`)
açmadan bu üç sayıyı kaynaktan çıkarmak mümkün değil — okuyucunun zincirin tamamını takip
etmesi gerekiyor. Gömme kaynağı kısaltıyor ama okuma yükünü aynı oranda azaltmıyor; kısa
kaynak, uzun bir sözleşmeyi hâlâ taşıyabiliyor.

#### Kaç Yöntem Yazmak Gerekiyor

Gömme yöntem eklemediğine göre, bileşik bir arayüzü karşılamak için bir tipin yazması gereken
en az yöntem sayısı, gömülen arayüzlerin yöntemlerinin toplamı olmalı — ne bir fazla ne bir
eksik. `reflect` bunu bir arayüz **tipine** sorarak saydı; aşağıdaki ölçüm aynı soruyu bir
arayüz **tanımına karşı sınanan bir tipe** sorarak, derleyicinin kendisinden alıyor. İkisi
aynı sayıya varmalı: `reflect`in söylediği sayı, derleyicinin kabul ettiği en küçük sayı.

```go
// arac.go — bir kaynak parcasinin derlenip derlenmedigini programin icinden sinar
package main

import (
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}
```

```go
// kac-yontem.go — bilesik arayuzu karsilamak icin gereken en az yontem sayisi olculuyor
package main

import "fmt"

func main() {
	ortak := `package sinama

type Okuyucu interface{ Oku() string }
type Yazici interface{ Yaz(m string) }

type OkuyupYazan interface {
	Okuyucu
	Yazici
}

`
	sifirYontem := ortak + `type Aday struct{}

var _ OkuyupYazan = Aday{}
`
	birYontem := ortak + `type Aday struct{}
func (Aday) Oku() string { return "" }

var _ OkuyupYazan = Aday{}
`
	ikiYontem := ortak + `type Aday struct{}
func (Aday) Oku() string { return "" }
func (Aday) Yaz(m string) {}

var _ OkuyupYazan = Aday{}
`
	fmt.Println("sifir yontemle derleniyor mu:", derlenirMi(sifirYontem))
	fmt.Println("bir yontemle derleniyor mu  :", derlenirMi(birYontem))
	fmt.Println("iki yontemle derleniyor mu  :", derlenirMi(ikiYontem))
}
```

```
sifir yontemle derleniyor mu: false
bir yontemle derleniyor mu  : false
iki yontemle derleniyor mu  : true
```

Sıfır yöntemli aday derlenmiyor, çünkü ikisi de eksik. Bir yöntemli aday (yalnız `Oku`) da
derlenmiyor, çünkü `Yaz` hâlâ eksik — `Okuyucu`yu karşılamak `OkuyupYazan`ı karşılamak
anlamına gelmiyor, gömme bir arayüzü diğerinin yerine geçirmiyor, ikisini birlikte istiyor.
İki yöntemli aday derleniyor ve bu, tam olarak `reflect`in bir önceki bölümde saydığı sayı.
Üç aday, tek bir eşiği (iki yöntem) iki yönden de sınıyor: bir eksik yetmiyor, tam sayı
yetiyor.

Bu eşik, üçüncü dersteki küçük arayüz ölçümüyle aynı ekseni izliyor: bir arayüzün istediği
yöntem sayısı ne kadar büyükse, onu karşılayan tipin yükü o kadar ağır. Gömme burada
yöntem sayısını **azaltmıyor**; yalnız iki ayrı sözleşmeyi tek bir adla anmayı
kolaylaştırıyor. `OkuyupYazan`ı karşılamanın maliyeti, `Okuyucu`yu ve `Yazici`yi ayrı ayrı
karşılamanın maliyetinin toplamı — gömme bu toplamı düşürecek bir kısayol sunmuyor, yalnız
adlandırmayı tek bir noktada topluyor.

**AR11.** Üç aday da aynı `Aday` adını taşıyor ve aynı `OkuyupYazan`a karşı sınanıyor;
tek değişen, `Aday`ın kendi yöntem kümesi. Bu, üç ayrı ölçümü karşılaştırılabilir kılıyor —
fark yalnız bir değişkende, geri kalan her şey sabit. Aynı disiplin dördüncü bir adayla da
sınanabilirdi — üç yöntem yazan, ikisi `OkuyupYazan`ın istediği isimlerle eşleşen, üçüncüsü
fazladan bir yöntem olan bir tip. Böyle bir tip yine derlenirdi, çünkü arayüz karşılaması bir
**alt küme** ilişkisidir: tipin yöntem kümesi arayüzün istediğini kapsıyorsa, fazlası bir
sorun oluşturmuyor.

#### Sınırlayıcı Ölçüm: Sözleşme Birleşir, Gerçekleştirim Birleşmez

Gömme kaynakta iki sözleşmeyi birleştiriyor, ama bu birleşme bir **gerçekleştirim** garantisi
vermiyor. Bileşik arayüzü karşılayan bir tip, gömülen iki arayüzü de ayrı ayrı karşılıyor —
bu yön kolay. Ama tersi doğru değil: iki ayrı tip, iki ayrı sözleşmeyi ayrı ayrı
karşılayabiliyor, bileşik sözleşmeyi ise hiçbiri karşılamıyor. Önceki bölümdeki eşik ölçümü
"kaç yöntem yeter" sorusunu bir tek tipin üzerinden sordu; bu ölçüm aynı soruyu birden çok
tip üzerinden sorup, tipler arasında hiçbir paylaşımın olmadığını gösteriyor.

```go
// iki-yon.go — bilesik sozlesmeyi karsilamanin iki yonu ayri ayri sinaniyor
package main

import "fmt"

type Okuyucu interface{ Oku() string }
type Yazici interface{ Yaz(m string) }

type OkuyupYazan interface {
	Okuyucu
	Yazici
}

type Dosya struct{}

func (Dosya) Oku() string  { return "icerik" }
func (Dosya) Yaz(m string) {}

type SadeceOkuyan struct{}

func (SadeceOkuyan) Oku() string { return "a" }

type SadeceYazan struct{}

func (SadeceYazan) Yaz(m string) {}

func main() {
	var d any = Dosya{}
	_, okurMu := d.(Okuyucu)
	_, yazarMi := d.(Yazici)
	_, bilesikMi := d.(OkuyupYazan)
	fmt.Println("ileri yon, Dosya Okuyucu mu       :", okurMu)
	fmt.Println("ileri yon, Dosya Yazici mi        :", yazarMi)
	fmt.Println("ileri yon, Dosya OkuyupYazan mi   :", bilesikMi)

	var okur any = SadeceOkuyan{}
	var yazar any = SadeceYazan{}
	_, okurTamam := okur.(Okuyucu)
	_, yazarTamam := yazar.(Yazici)
	_, okurBilesikMi := okur.(OkuyupYazan)
	_, yazarBilesikMi := yazar.(OkuyupYazan)
	fmt.Println("geri yon, SadeceOkuyan Okuyucu mu    :", okurTamam)
	fmt.Println("geri yon, SadeceYazan Yazici mi      :", yazarTamam)
	fmt.Println("geri yon, SadeceOkuyan OkuyupYazan mi:", okurBilesikMi)
	fmt.Println("geri yon, SadeceYazan OkuyupYazan mi :", yazarBilesikMi)
}
```

```
ileri yon, Dosya Okuyucu mu       : true
ileri yon, Dosya Yazici mi        : true
ileri yon, Dosya OkuyupYazan mi   : true
geri yon, SadeceOkuyan Okuyucu mu    : true
geri yon, SadeceYazan Yazici mi      : true
geri yon, SadeceOkuyan OkuyupYazan mi: false
geri yon, SadeceYazan OkuyupYazan mi : false
```

İleri yönde tek bir tip (`Dosya`) her üç arayüzü de karşılıyor — bileşik sözleşmeyi
karşılayan bir tip, parçalarını da otomatik olarak karşılıyor, çünkü bileşik sözleşmenin
yöntem kümesi parçaların üst kümesi. `Dosya`nın `Oku` ve `Yaz` yöntemlerini yazmış olması
tek başına yeterli; `Okuyucu`yu, `Yazici`yi ve `OkuyupYazan`ı ayrı ayrı karşılamak için üç
kez farklı kod yazmasına gerek kalmıyor.

Geri yönde durum farklı: `SadeceOkuyan` yalnız `Okuyucu`yu, `SadeceYazan` yalnız `Yazici`yi
karşılıyor ve ikisi de doğru. Ama `OkuyupYazan`ı karşılayan **hiçbiri** yok — ne
`SadeceOkuyan`, ne `SadeceYazan`. İki sözleşme ayrı ayrı karşılanmış olması, bileşik
sözleşmenin de karşılandığı anlamına gelmiyor; bunun için tek bir tipin **her iki** yöntemi
birden yazması gerekiyor. Programda `SadeceOkuyan` ve `SadeceYazan`ın ikisi birden var olsa
bile, `OkuyupYazan` isteyen bir yer bu ikisinden hiçbirini kabul etmiyor — ikisinin
"toplamı" diye bir şey tip sisteminde yok.

**AR12.** `SadeceOkuyan` ile `SadeceYazan`ın birlikte "bir yerlerde" `Oku` ve `Yaz`
yöntemlerinin ikisini de karşılıyor olması, programın hiçbir yerinde bir anlam taşımıyor —
Go'nun tip sistemi iki ayrı tipin yöntemlerini otomatik olarak birleştiren bir düzenek
sunmuyor. Gömme yalnız **arayüz tanımını** birleştiriyor; iki farklı somut tipin
davranışlarını birleştirmek isteyen bir üçüncü tip, kendi yöntemlerini kendisi yazmak ya da
K01'in Yapılar dersindeki gömme yoluyla iki somut tipi kendi içine almak zorunda.

Bu ayrım, yapı gömmesiyle arayüz gömmesi arasındaki temel farkı açıklıyor. Bir yapı başka
bir yapıyı gömdüğünde, gömülü tipin gerçek bir değeri — kendi alanları ve yöntemleriyle
birlikte — dış yapının içinde somut olarak duruyor; K01'in Yapılar dersinde gömülü
yöntemlerin dış tipten yazılmadan çağrılabilmesinin nedeni buydu. Bir arayüz başka bir
arayüzü gömdüğünde ise ortada hiçbir değer yok, yalnızca bir imza listesi birleşiyor. Arayüz
gömmesi bir değer taşımıyor, yalnızca bir istek taşıyor; bu isteği kimin karşılayacağı ayrı
bir soru ve bu sorunun yanıtı yalnızca tek bir somut tipin kendi yöntemleriyle (ya da o
tipin kendi yapı gömmeleriyle) verilebiliyor. İki gömme de aynı sözcüğü paylaşıyor çünkü
ikisi de bir adı yazıp onun taşıdığı her şeyi kazanma fikrine dayanıyor; ama biri bir değeri
taşırken öbürü yalnızca bir sözleşmeyi taşıyor.

#### Özet

- Arayüz gömme, bir arayüzün gövdesine başka arayüzlerin adlarını yazmaktan ibaret; bağ
  kaynakta açıkça duruyor, bu kursta bildirim sınıfının en açık örneği.
- Gömme ile kurulan bir sözleşme, aynı yöntemleri elle yeniden yazan bir sözleşmeyle
  `reflect` düzeyinde birebir aynı yöntem sayısını istiyor — gömme hiçbir yeni yöntem
  eklemiyor.
- Bileşik bir arayüzü karşılamak için bir tipin yazması gereken en az yöntem sayısı,
  gömülen arayüzlerin toplamına eşit: bir eksik yetmiyor, tam sayı yetiyor.
- Bileşik arayüzü karşılayan bir tip gömülen arayüzlerin her birini de karşılıyor, ama
  tersi doğru değil: iki ayrı tip iki sözleşmeyi ayrı ayrı karşılayıp bileşik sözleşmeyi
  hiçbiri karşılamayabiliyor.
- Arayüz gömmesi bir değer taşımıyor, yalnızca bir sözleşme taşıyor; yapı gömmesi (K01'in
  Yapılar dersi) ise gerçek bir değeri taşıyor — aynı sözcük, iki ayrı düzenek.

#### Sonraki Adım

Bu derste sözleşmeyi tanımlayan taraf, sözleşmeyi karşılayacak tipten önce davranmıştı:
`Okuyucu` ve `Yazici`, `Dosya` yazılmadan önce zaten oradaydı. Ama üçüncü dersteki
`Depolayici` de, birinci dersteki `Kisa` da, karşıladıkları tipin kaynağında hiç
geçmiyordu — sözleşmeyi tanımlayan taraf, kullanan taraftı. Bu iki kalıp arasındaki fark bir
tesadüf değil, bir tercih: bazen sözleşmeyi üreten taraf yazar (bu ders), bazen tüketen
taraf (birinci ve üçüncü ders). Sıradaki ders bu tercihi bir tasarım kararına dönüştürüyor:
bir arayüzü kim tanımlamalı, üreten paket mi, tüketen taraf mı ve bunun ölçülebilir bir
karşılığı var mı?

### Arayüz Tasarımı

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/arayuz-tasarimi

Tüketici tarafında tanımlama ölçülüyor: dört yöntemli bir tip hiçbir arayüze atıf yapmadan yazılıyor, dört ayrı tüketici birbirinden habersiz kendi tek yöntemli arayüzünü tanımlıyor ve aynı tip dördünü de karşılıyor — üreten tarafın kaynağında bu dört adın hiçbirine atıf yok. Sınırlayıcı ölçüm: aynı sözleşme iki tüketicide iki ayrı adla tanımlandığında birbirinin yerine geçemez görünüyor, ama tip sistemi ikisini de aynı değere doğrudan kabul ediyor.

Önceki ders, sözleşmeyi tanımlayan tarafın sözleşmeyi karşılayacak tipten **önce**
davrandığı bir örnekle açıldı: `Okuyucu` ve `Yazici`, `Dosya` yazılmadan önce zaten
oradaydı. Ama kursun ilk dersindeki `Kisa`, üçüncü dersindeki `Depolayici` tam tersini
yaptı — ikisi de karşıladıkları tipin kaynağında hiç geçmedi. Bu ders o gözlemi bir
tasarım kararına dönüştürüyor: bir arayüzü kim tanımlamalı, üreten taraf mı, tüketen taraf
mı ve bu seçimin ölçülebilir bir karşılığı var mı?

Bu soru bir tercih değil, birinci dersin ikinci iddiasının doğal uzantısı. Kaynak, bir
tipin bir arayüzü karşıladığını hiç söylemiyorsa, o arayüzün üretici tarafın kaynağında
**bulunması gerekmiyor** demektir — tüketici, üreticinin hiç bilmediği bir sözleşmeyi
sonradan yazıp aynı tipe uygulayabiliyor. Bu ders bu imkânı bir kez daha, bu kez ölçekte
ve bir bedelle birlikte gösteriyor: tek bir tip, birden çok bağımsız tüketici tarafından
kaç ayrı sözleşmeyle sarılabiliyor ve bu esnekliğin karşılığında hangi görünmezlik
ortaya çıkıyor.

#### Tüketici Tarafında Tanımlama

Aşağıdaki ölçüm, dört yöntemli bir tipi (`Depo`) hiçbir arayüze atıf yapmadan yazıyor.
Ardından dört ayrı tüketici, birbirinden habersiz, kendi tek yöntemli arayüzünü
tanımlıyor. Ölçüm, `Depo`nun bu dört arayüzden kaçını karşıladığını sayıyor.

```go
// depo.go — uretici tarafin kodu, hicbir arayuze atif yapmadan yaziliyor
package main

import "fmt"

type Depo struct{ ogeler []string }

func (d *Depo) Ekle(o string)     { d.ogeler = append(d.ogeler, o) }
func (d *Depo) Sil(o string) bool { return true }
func (d *Depo) Listele() []string { return d.ogeler }
func (d *Depo) Boyut() int        { return len(d.ogeler) }

// Dort ayri tuketicinin kendi tanimladigi, birbirinden habersiz dort arayuz.
type Yazici interface{ Ekle(o string) }
type Temizleyici interface{ Sil(o string) bool }
type Raporlayici interface{ Listele() []string }
type Olculebilir interface{ Boyut() int }
type Yedekleyici interface{ Yedekle() error }

func main() {
	var d any = &Depo{}
	_, yaziciMi := d.(Yazici)
	_, temizleyiciMi := d.(Temizleyici)
	_, raporlayiciMi := d.(Raporlayici)
	_, olculebilirMi := d.(Olculebilir)
	_, yedekleyiciMi := d.(Yedekleyici)

	sayisi := 0
	for _, tamam := range []bool{yaziciMi, temizleyiciMi, raporlayiciMi, olculebilirMi, yedekleyiciMi} {
		if tamam {
			sayisi++
		}
	}
	fmt.Println("Yazici mi      :", yaziciMi)
	fmt.Println("Temizleyici mi :", temizleyiciMi)
	fmt.Println("Raporlayici mi :", raporlayiciMi)
	fmt.Println("Olculebilir mi :", olculebilirMi)
	fmt.Println("Yedekleyici mi :", yedekleyiciMi)
	fmt.Println("bes adaydan karsilanan sayisi:", sayisi)
}
```

```
Yazici mi      : true
Temizleyici mi : true
Raporlayici mi : true
Olculebilir mi : true
Yedekleyici mi : false
bes adaydan karsilanan sayisi: 4
```

`Depo`nun kaynağı dört satırdan ibaret: `Ekle`, `Sil`, `Listele`, `Boyut`. Bu dört satırda
`Yazici`, `Temizleyici`, `Raporlayici`, `Olculebilir` adlarının hiçbiri geçmiyor — `Depo`
yazılırken bu dört arayüz henüz var olmayabilirdi bile. Buna rağmen `Depo` dördünü de
karşılıyor, çünkü karşılamanın koşulu yalnızca yöntem imzalarının eşleşmesi. Beşinci aday
(`Yedekleyici`) bilinçli olarak eklendi: `Depo`nun yazmadığı bir yöntem (`Yedekle`)
istiyor ve `false` dönüyor — ölçüm yalnız uyanları değil, uymayanı da gösteriyor, ki
sonuç seçici bir örnekleme değil gerçek bir sınır olsun.

Dört arayüzün her biri farklı bir tüketici senaryosunu temsil ediyor: `Yazici` yalnızca
yeni öge ekleyen bir kod parçasının ihtiyacı, `Temizleyici` yalnızca silme yapan bir
işlevin, `Raporlayici` yalnızca listeleme yapan bir görünümün, `Olculebilir` yalnızca
boyut sınayan bir denetimin. Bu dört tüketici birbirini hiç görmemiş olabilir, ayrı
paketlerde, ayrı ekipler tarafından yazılmış olabilir — tek ortak noktaları `Depo`nun
kendisi. Yine de dördü de kendi dar sözleşmesini kurup `Depo`yu ona uydurabiliyor, çünkü
uyum kontrolü merkezî bir kayıt yerine her tüketicinin kendi bakış açısından yapılıyor.

**AR13.** Dört tüketici arayüzü tek bir yöntem istiyor; bu bilinçli bir seçim. Kursun
ilk dersindeki küçük arayüz ölçümü, yöntem sayısı arttıkça karşılayan tip sayısının
düştüğünü göstermişti. Burada tersi kullanılıyor: yöntem sayısını bire indirmek,
`Depo`nun tek bir yöntemi olan her tüketiciyi karşılamasını neredeyse garantiliyor —
tüketici tarafında tanımlamanın pratikte bu kadar sık işlemesinin bir nedeni de bu.

Bu düzende üreten paket bir kez yazılıyor ve hiç değişmiyor; her yeni tüketici kendi
arayüzünü kendi ihtiyacına göre tanımlıyor. Kursun terminolojisinde bu, arayüzün
karşılanmasının bağını **kullanım yerine** taşıyan aynı mekanizma — birinci dersteki
`Kisa`nın ve üçüncü dersteki `Depolayici`nin yaptığı da tam olarak buydu. Fark, bu
dersin onu tek bir örnekten çıkarıp bir tasarım gözlemine dönüştürmesi: dört tüketici,
dört farklı ihtiyaç, dört farklı ad ve üretici tarafın bunların hiçbirinden haberi yok.
Tasarım İlkeleri kursunun arayüz ayrımı ilkesi bu deseni bir öğüt olarak sunuyor; bu ders
öğüt vermiyor, deseni sayıyor.

Sayının kendisi de bir şey söylüyor: `Depo` dört arayüzü karşılıyor, ama `Depo`nun kendi
kaynağında dört değil **sıfır** arayüz adı geçiyor. `Depo`nun kaynağı hiç değişmeden,
`Depo`nun mevcut dört yönteminin herhangi bir alt kümesini isteyen yeni bir arayüz her an
tanımlanabilir ve `Depo` onu da karşılamaya devam eder — beşinci, altıncı, onuncu
tüketici için de aynı kural geçerli, üretici tarafın kaynağının bundan haberi olması
gerekmiyor. `Depo`yu yazan taraf, kaç tüketicinin kendisini kaç farklı sözleşmeyle
sardığını bilmek zorunda değil.

#### Tüketicinin Kendi Bileşiği

Dördüncü ders gömmenin sözleşmeleri **üreten tarafta** birleştirdiğini ölçmüştü:
`Okuyucu` ve `Yazici`, `Dosya` yazılmadan önce zaten `OkuyupYazan` içinde birleşmişti. Aynı
birleşme tüketici tarafında da mümkün — yalnız bu kez birleştiren taraf üretici değil,
tüketicinin kendisi.

```go
// bilesik-tuketici.go — tuketici, uretici tipin dort yontemini kendi sozlesmesinde topluyor
package main

import "fmt"

type Depo struct{ ogeler []string }

func (d *Depo) Ekle(o string)     { d.ogeler = append(d.ogeler, o) }
func (d *Depo) Sil(o string) bool { return true }
func (d *Depo) Listele() []string { return d.ogeler }
func (d *Depo) Boyut() int        { return len(d.ogeler) }

type Yazici interface{ Ekle(o string) }

// Besinci tuketici, dort yontemi kendi bilesik sozlesmesinde topluyor; gomme kullanmiyor,
// dort imzayi elle yaziyor.
type TamDepo interface {
	Ekle(o string)
	Sil(o string) bool
	Listele() []string
	Boyut() int
}

func main() {
	var d any = &Depo{}
	_, yaziciMi := d.(Yazici)
	_, tamDepoMu := d.(TamDepo)
	fmt.Println("dar sozlesme,  Yazici mi   :", yaziciMi)
	fmt.Println("genis sozlesme, TamDepo mu  :", tamDepoMu)
}
```

```
dar sozlesme,  Yazici mi   : true
genis sozlesme, TamDepo mu  : true
```

`TamDepo`, `Depo`nun dört yönteminin tamamını tek bir sözleşmede topluyor, ama bunu
gömerek değil, dördünü tek tek yazarak yapıyor — `TamDepo` kendi başına bir gömme
zinciri değil. Buna rağmen `Depo` onu da karşılıyor, tıpkı tek yöntemli `Yazici`yi
karşıladığı gibi. Dördüncü dersteki ölçüm gömmenin **üretici tarafın** sözleşmesini nasıl
birleştirdiğini gösterdi; bu ölçüm aynı birleşmenin **tüketici tarafında**, üreticinin hiç
haberi olmadan da kurulabildiğini gösteriyor. İki model burada çakışmıyor, aynı tipte yan
yana duruyor: `Depo` hem en dar sözleşmeyi (`Yazici`) hem en geniş sözleşmeyi (`TamDepo`)
aynı anda karşılıyor, çünkü ikisi de aynı dört yöntemin farklı alt kümelerini istiyor.

**AR14.** `TamDepo`nun `Depo`nun tam olarak yazdığı dört yöntemi istemesi bilinçli bir
seçim — sözleşme üreticinin yöntem kümesini aşsaydı, `Depo` onu karşılamazdı. Ölçümün
gösterdiği şey sınırsız bir uyum değil: tüketici ne kadar geniş bir sözleşme yazarsa
yazsın, üreticinin gerçekte yazdığı yöntemlerin dışına çıkamıyor.

Bu, tüketici tarafında tanımlamanın gerçek sınırını netleştiriyor: tüketici, üreticinin **neyi
yazdığını** seçemiyor, yalnızca üreticinin yazdıklarından **hangisini istediğini** seçebiliyor.
`TamDepo` beşinci bir yöntem (`Yedekle`) isteseydi, yukarıdaki `Yedekleyici` gibi karşılanmazdı —
geniş bir sözleşme yazmak, üretici tarafın kaynağına yeni bir yöntem eklemenin yerine geçmiyor.
Tüketici tarafında tanımlamanın verdiği özgürlük, var olan yöntemlerin **seçimi** üzerinedir, var
olmayan yöntemlerin **icadı** üzerinde değil.

#### Sınırlayıcı Ölçüm: Aynı Sözleşme, İki Ayrı Ad

Tüketici tarafında tanımlama bedelsiz değil. İki ayrı tüketici aynı yöntemi isteyen iki
ayrı arayüz tanımladığında, bu iki arayüz kaynakta hiçbir ortak noktaya sahip görünmüyor
— ikisi de kendi paketinde, kendi adıyla, birbirinden habersiz duruyor. Ama Go'nun tip
sistemi arayüzleri **adına göre değil yöntem kümesine göre** karşılaştırıyor; bu yüzden
iki ayrı ad, aynı imzayı taşıdığı sürece birbirinin yerine geçiyor.

```go
// iki-ad.go — ayni imzayi tasiyan iki ayri arayuz adi, birbirinin yerine geciyor
package main

import "fmt"

type Depo struct{ ogeler []string }

func (d *Depo) Ekle(o string) { d.ogeler = append(d.ogeler, o) }

// Iki tuketicinin birbirinden habersiz tanimladigi, ayni imzali iki arayuz.
type Yazici interface{ Ekle(o string) }
type Girdi interface{ Ekle(o string) }

func birinciTuketici(y Yazici) string { y.Ekle("A"); return "Yazici uzerinden eklendi" }
func ikinciTuketici(g Girdi) string   { g.Ekle("B"); return "Girdi uzerinden eklendi" }

func main() {
	d := &Depo{}

	var g Girdi = d
	var y Yazici = g // Girdi tipindeki bir deger dogrudan Yazici degiskenine atanabiliyor mu
	fmt.Println("Girdi degeri Yazici degiskenine atanabildi mi:", y != nil)

	fmt.Println(birinciTuketici(g)) // Girdi degeri Yazici parametresine geciyor
	fmt.Println(ikinciTuketici(y))  // Yazici degeri Girdi parametresine geciyor
	fmt.Println("toplam eklenen oge sayisi:", len(d.ogeler))
}
```

```
Girdi degeri Yazici degiskenine atanabildi mi: true
Yazici uzerinden eklendi
Girdi uzerinden eklendi
toplam eklenen oge sayisi: 2
```

`Yazici` ile `Girdi`, iki ayrı ad taşıyan, birbirinden habersiz yazılmış iki ayrı arayüz.
Aralarında hiçbir gömme, hiçbir ortak tanım yok. Buna rağmen `var y Yazici = g` satırı
derleniyor — bir `Girdi` değeri doğrudan bir `Yazici` değişkenine atanabiliyor — ve
`birinciTuketici`, `Yazici` beklerken bir `Girdi` değeri kabul ediyor. Fark yalnızca adda;
Go'nun tip sistemi arayüzleri isimleriyle değil, istedikleri yöntemlerle tanımlıyor. İki
farklı tüketici aynı yöntemi isteyen iki farklı arayüz tanımladığında, bu iki tanım
istemeden **eşdeğer** oluyor.

**AR15.** Bu satır kaynaktan okunarak tahmin edilemez: `Yazici` ve `Girdi` tanımlarına
tek tek bakan biri, aralarında bir ilişki olduğunu göremiyor, çünkü ilişki hiçbir yerde
yazılı değil — yalnızca ikisinin yöntem kümesi karşılaştırıldığında ortaya çıkıyor.
Ölçüm bu köru noktayı doğrudan koşturarak gösteriyor; kaynağı okumak yetmiyor, iki
tanımı yan yana koyup imzaları karşılaştırmak gerekiyor.

Bu eşdeğerliğin sınırı da net: imzalar **birebir** aynı olmak zorunda, yalnız benzemesi
yetmiyor. Aşağıdaki ölçüm bunu doğrudan derleyiciye soruyor.

```go
// sinir.go — ayni imza gecerli kiliyor, farkli donus tipi reddediyor
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

func main() {
	ortak := `package sinama

type Yazici interface{ Ekle(o string) }

func f(y Yazici) {}

`
	ayniImza := ortak + `type Girdi interface{ Ekle(o string) }

func g(x Girdi) { f(x) }
`
	farkliImza := ortak + `type Aktarici interface{ Ekle(o string) error }

func g(x Aktarici) { f(x) }
`
	fmt.Println("ayni imzali arayuz gecerli mi  :", derlenirMi(ayniImza))
	fmt.Println("farkli imzali arayuz gecerli mi:", derlenirMi(farkliImza))
}
```

```
ayni imzali arayuz gecerli mi  : true
farkli imzali arayuz gecerli mi: false
```

`Girdi`, `Yazici` ile birebir aynı imzayı (`Ekle(string)`) taşıdığı için `Yazici`
beklenen yerde kabul ediliyor. `Aktarici` ise aynı adı (`Ekle`) taşısa da farklı bir
dönüş tipi (`error`) istiyor ve bu fark tek başına eşdeğerliği bozmaya yetiyor —
derleniyor mu sorusu `false`e dönüyor. Eşdeğerlik ada değil, imzanın **her parçasına**
bakıyor: yöntem adı, parametre tipleri, dönüş tipleri, hepsi birden eşleşmek zorunda.
İki tüketicinin bağımsız yazdığı arayüzlerin örtük olarak eşleşmesi, ancak ikisi de
aynı sözleşmeyi harfiyen tekrarladığında oluyor; küçük bir fark bile bu örtük bağı
koparıyor.

Bu, tüketici tarafında tanımlamanın maliyetidir: bağımsız tüketiciler bağımsız
sözleşmeler yazdığında, aynı sözleşmenin birden çok kez, birbirinden habersiz
tekrarlanması hiçbir yerde engellenmiyor. Sonuç yanlış değil — iki arayüz gerçekten
eşdeğer ve tip sistemi bunu doğru işliyor — ama kaynağı okuyan biri için **görünmez**
bir eşdeğerlik bu. Dört arayüzün her biri kendi tüketicisine göre en dar sözleşmeyi
kurduğu için yararlı olan aynı özgürlük, iki tüketici aynı ihtiyacı ayrı ayrı
tanımladığında fazladan bir sözleşmeye dönüşüyor.

#### Özet

- Tüketici tarafında tanımlamada, üreten taraf hiçbir arayüz yazmadan bir tip üretiyor
  ve tüketen taraf kendi arayüzünü kendi ihtiyacına göre tanımlıyor.
- `Depo` dört tüketici arayüzünün dördünü de karşılıyor, kaynağında bu dört adın
  hiçbirine atıf yok; beşinci bir aday (`Yedekleyici`) karşılanmayarak sınırı gösteriyor.
- Küçük, tek yöntemli tüketici arayüzleri bu deseni pratikte kolaylaştırıyor — yöntem
  sayısı düştükçe karşılanma olasılığı artıyor.
- Go'nun arayüz karşılaştırması ada değil yöntem kümesine bakıyor; bu yüzden aynı imzayı
  taşıyan iki ayrı adlı arayüz, kaynakta hiçbir bağlantı yazılı olmasa da birbirinin
  yerine geçiyor.
- Bu, tüketici tarafında tanımlamanın bedelidir: bağımsız tüketiciler aynı sözleşmeyi
  habersizce yineleyebiliyor ve bu yineleme yalnızca imzalar karşılaştırıldığında görünür
  oluyor.
- Eşdeğerliğin sınırı kesin: yöntem adı aynı olsa bile parametre ya da dönüş tipi
  farklıysa, iki arayüz birbirinin yerine geçmiyor; derleyici bu farkı doğrudan
  reddediyor.

#### Sonraki Adım

Arayüzü tüketicinin tanımlaması, bu kurs boyunca ölçülen bir gerçeğin doğal sonucu: bağ
kaynakta hiç yazılmıyorsa, sözleşmeyi kimin yazdığı da üretici tarafın kararına
bağlanmıyor. Beş ders boyunca ölçülen çerçeve (bildirim, kullanım yeri, değer) burada da
geçerliydi: gömme bildirimde, tüketici tarafında tanımlama kullanım yerinde, ikisi de
aynı örtük gerçekleştirim ilkesinin iki farklı yüzü. Sıradaki konu bu modelin en çok
kullanıldığı yere bakıyor. Go'da hata bir **arayüzdür** ve her hata değeri onu örtük
karşılar — üç yöntemli, dört yöntemli bir sözleşme değil, tek bir yöntemli en dar
sözleşme. Bir sonraki dersin sorusu: bir hata değerinin kimliği nerede duruyor ve bir
hatayı hata yapan şey nedir?

## Hata Yönetimi

Hatanın bir değer olması ve arayüzü örtük karşılaması, nöbetçi hataların kimliğini değerinde taşıması, sarmalamanın bağlam eklerken zinciri koruması ve çözmenin hedefi zincirde araması, paniğin ve kurtarmanın hangi durumda meşru hangisinde kötüye kullanım olduğu.

### Hata Arayüzü

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/hata-arayuzu

Hata Go'da sıradan bir değerdir ve error arayüzünü örtük karşılar: Error() string yazan her tip, kaynağında bunu hiç söylemeden hata olur. Üç tip bunu doğrular, dinamik tipleri %T ile okunur; değer alıcılı bir hatanın yapısal eşitliği işaretçi alıcılı bir hatanın kimlik eşitliğinden ayrılır. Sınırlayıcı ölçüm: nil bir işaretçi error arayüzüne konduğunda arayüzün kendisi nil değildir.

Önceki konunun son dersi bir tüketicinin, üreten tarafın kaynağında hiç yazmadığı bir arayüzü
tanımlayabildiğini ölçtü — bağın kaynakta hiç yazılmamasının doğal bir sonucuydu bu. Bu ders
aynı modelin Go'da en çok kullanıldığı yere bakıyor: standart kitaplığın kendisi tek bir arayüz
etrafında kurulu, `error` ve her hata değeri onu tam olarak aynı biçimde, hiçbir bildirim
yazmadan karşılıyor.

`error`, Go'da sıradan bir dönüş değeridir ve sıradan bir arayüzdür — tek yöntemi var:
`Error() string`. Bu dersin sorusu, önceki konudan taşınan aynı soru: bu arayüzü karşılama bağı
nerede kuruluyor? Yanıtı bulmak için üç ayrı tip, hiçbirinin kaynağında `error` sözcüğü
geçmeden, hata olarak dönecek.

Fonksiyonlar dersi (M09/K01) `error`i zaten sıradan bir dönüş değeri olarak ölçtü ve çağıranın
onu hiç yakalamadan yok saymasının hiçbir şekilde reddedilmediğini gösterdi — o gözlem burada
tekrar kurulmuyor. Bu ders, o dönüş değerinin **ne olduğuna** ve bir tipin ne zaman "bir hata"
sayıldığına bakıyor. Sorulan soru, kursun geri kalanında da aynı kalacak: bir bağ kaynakta açıkça
mı yazılı, derleyicinin baktığı kullanım yerinde mi kuruluyor, yoksa yalnız çalışma zamanında
değerin içinde mi taşınıyor?

Bu konu bu soruyu dört adımda ilerletiyor. Bu ders hatanın neden bir değer olduğunu ve arayüzü
nasıl karşıladığını ölçüyor; sıradaki ders bir hatanın kimliğinin nerede durduğunu, üçüncü ders
bir hataya eklenen bağlamın zincir boyunca nasıl korunduğunu, dördüncü ders de imzada hiç
görünmeyen bir çıkış yolunun — paniğin — bağının nerede kurulduğunu (ya da kurulmadığını)
ölçüyor.

#### Üç Tip, Sıfır Bildirim

Önceki konu örtük karşılamayı Arayüz Kavramı dersinde tek bir tip üzerinde ölçtü; bu ders aynı
düzeneği hata değerlerine uyguluyor. Aşağıdaki üç tip birbirinden bağımsız yazıldı: biri değer
alıcılı bir yapı, biri işaretçi alıcılı bir yapı, biri de tamsayı üzerine kurulu adlandırılmış
bir tip. Üçünün de ortak noktası tek satır: `Error() string` imzasına uyan bir yöntem.

Üç tipin özellikle farklı biçimlerde seçilmesi rastgele değil. `error` arayüzü standart
kitaplıkta o kadar yaygın kullanılıyor ki, hemen her tip biçimi — değer, işaretçi, adlandırılmış
temel tip, hatta boş bir yapı — onu karşılayabilir hâle geliyor. Ölçümün amacı, bu çeşitliliğin
karşılamayı hiç etkilemediğini göstermek: arayüz tek bir yöntem istiyor, o yöntemi hangi tipin
nasıl taşıdığıyla ilgilenmiyor.

```go
// bag.go — hata bagi kullanim yerinde mi kuruluyor, degerin icinde mi
package main

import "fmt"

var kullanimYeri, deger int

func gozle(ad, yer, kanit string) {
	switch yer {
	case "kullanim":
		kullanimYeri++
	default:
		deger++
	}
	fmt.Printf("%-40s %-9s %s\n", ad, yer, kanit)
}

func ozet() {
	fmt.Printf("bag: %d kullanim yeri, %d deger\n", kullanimYeri, deger)
}
```

```go
// olcum.go — hata bir deger: uc tip ondan hic bahsetmeden Error() string yazip hata oluyor
package main

import "fmt"

type BicimHatasi struct{ Alan string }

func (b BicimHatasi) Error() string { return "bicim gecersiz: " + b.Alan }

type ErisimHatasi struct{ Kod int }

func (e *ErisimHatasi) Error() string { return fmt.Sprintf("erisim reddedildi: kod %d", e.Kod) }

type SayacHatasi int

func (s SayacHatasi) Error() string { return fmt.Sprintf("sayac tasti: %d", int(s)) }

func main() {
	var h1 error = BicimHatasi{Alan: "ad"}
	var h2 error = &ErisimHatasi{Kod: 403}
	var h3 error = SayacHatasi(9)

	gozle("uc tipin hata arayuzunu karsilamasi", "kullanim",
		"kaynaklarinda 'error' sozcugu hic gecmiyor")
	gozle("hata degerinin dinamik tipi", "deger", fmt.Sprintf("%T, %T, %T", h1, h2, h3))

	var a error = BicimHatasi{Alan: "ad"}
	var b error = BicimHatasi{Alan: "ad"}
	gozle("deger alicili iki hatanin esitligi", "deger", fmt.Sprint(a == b))

	var c error = &ErisimHatasi{Kod: 403}
	var d error = &ErisimHatasi{Kod: 403}
	gozle("isaretci alicili iki hatanin esitligi", "deger", fmt.Sprint(c == d))

	ozet()
}
```

```
uc tipin hata arayuzunu karsilamasi      kullanim  kaynaklarinda 'error' sozcugu hic gecmiyor
hata degerinin dinamik tipi              deger     main.BicimHatasi, *main.ErisimHatasi, main.SayacHatasi
deger alicili iki hatanin esitligi       deger     true
isaretci alicili iki hatanin esitligi    deger     false
bag: 1 kullanim yeri, 3 deger
```

**HT1.** Üç tipten hiçbiri `error` sözcüğünü kaynağında hiç anmıyor; `BicimHatasi`,
`ErisimHatasi` ve `SayacHatasi` yalnız `Error() string` imzasına uyan bir yöntem yazıyor ve bu
onları hata yapmaya yetiyor. Bağ, `var h1 error = BicimHatasi{...}` satırında, değer bir
`error` değişkenine **konduğu** anda kuruluyor — kaynakta hiçbir yerde "bu tip hata arayüzünü
karşılıyor" yazmıyor. `%T` ile okunan dinamik tip, üçünün de karşılamayı gerçekten
tamamladığını doğruluyor: değer tipi `main.BicimHatasi` olarak, işaretçi tipi
`*main.ErisimHatasi` olarak, adlandırılmış tip de `main.SayacHatasi` olarak görünüyor.

**HT2.** Son iki satır aynı `BicimHatasi` değerinden iki bağımsız kopya, aynı `ErisimHatasi`
değerinden de iki bağımsız işaretçi üretip karşılaştırıyor. Değer alıcılı hata **yapısal**
karşılaştırılıyor: iki ayrı değişken, alanları aynı olduğu için eşit çıkıyor. İşaretçi alıcılı
hata ise **kimlik** karşılaştırılıyor: iki ayrı `&ErisimHatasi{...}` çağrısı iki ayrı adres
üretiyor ve eşitlik `false` dönüyor, alanları birebir aynı olsa bile. Bu fark rastgele değil —
sıradaki ders paket düzeyinde tanımlanan bir hata değerinin kimliğini tam olarak bu ayrımın
üzerine kuruyor.

Bu iki satır aynı zamanda küçük bir uyarı taşıyor: `error` arayüzünün karşılaştırma operatörünü
(`==`) desteklemesi, iki hatanın "aynı hata" olduğu anlamına gelmiyor — yalnızca arayüzün
taşıdığı çiftin (dinamik tip, dinamik değer) iki tarafta da eşit olduğu anlamına geliyor. Değer
alıcılı bir hata için bu, alanların eşitliğine indirgeniyor; alanlardan biri bile farklıysa
karşılaştırma `false` dönüyor. Bu ayrım aşağıdaki bölümde, aynı yöntemin hangi tipin yöntem
kümesine girdiği sorusuyla birleşiyor.

#### Yöntem Kümesi: Karşılayan Değer Değil Tiptir

Go Temelleri kursunun İşaretçiler dersi, bir yöntemin değer alıcı mı işaretçi alıcı mı olduğunun
çağrı yazımından değil alıcı tanımından geldiğini ölçtü — o mekanik burada tekrar kurulmuyor. Ama
bu dersin sorusu için tek bir sonucu önemli: `ErisimHatasi`nin `Error` yöntemi işaretçi alıcılı
yazıldığında, hangi tip gerçekten `error` arayüzünü karşılıyor — `ErisimHatasi`nin kendisi mi,
yoksa yalnız `*ErisimHatasi` mi? Yanıtı derleyiciye sormak, K01'in `derlenirMi` aracını ödünç
almayı gerektiriyor: bir kaynak parçası geçici bir pakete yazılıyor, derleniyor ve yalnız geçip
geçmediği okunuyor.

Bu araç yalnızca geçip geçmediğini okuyor, hata metnini hiç okumuyor — ölçülen şey "neden
reddedildi" değil, "reddedildi mi". İki kaynak parçası da aynı `ErisimHatasi` tanımını ve aynı
boş işlev gövdesini paylaşıyor; aralarındaki tek fark, `error` değişkenine konan değerin `.`
işareti taşıyıp taşımadığı. Bu, arayüz karşılamasının sözdiziminde ne kadar küçük bir ayrıma
dayanabildiğini de gösteriyor.

```go
// yontemkumesi.go — isaretci alicili yontem, sadece isaretci tipini hata arayuzune sokuyor
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

const tipler = `
package sinama
type ErisimHatasi struct{ Kod int }
func (e *ErisimHatasi) Error() string { return "erisim reddedildi" }
`

func main() {
	degerAtama := tipler + "func F() { var _ error = ErisimHatasi{Kod: 1} }"
	isaretciAtama := tipler + "func F() { var _ error = &ErisimHatasi{Kod: 1} }"

	gozle("deger tipini error degiskenine atama", "kullanim", fmt.Sprint(derlenirMi(degerAtama)))
	gozle("isaretci tipini error degiskenine atama", "kullanim", fmt.Sprint(derlenirMi(isaretciAtama)))
	ozet()
}
```

```
deger tipini error degiskenine atama     kullanim  false
isaretci tipini error degiskenine atama  kullanim  true
bag: 2 kullanim yeri, 0 deger
```

**HT3.** `ErisimHatasi` değeri, `error`e atanmaya çalıştığında derlemeyi durduruyor;
`*ErisimHatasi` işaretçisi aynı satırda sorunsuz geçiyor. İkisi de aynı alanları taşıyor,
ikisi de aynı `Kod` değerine sahip olabilir — ayrım verinin kendisinde değil, hangi tipin
`Error() string` yöntemine sahip olduğunda. Bir yöntem işaretçi alıcılı yazıldığında, o
yöntem yalnızca işaretçi tipinin **yöntem kümesine** giriyor; değer tipinin yöntem kümesi
onu hiç içermiyor.

**HT4.** Bu, örtük karşılamanın nerede kurulduğunu daha da netleştiriyor: bağ yalnızca
"bir `Error() string` yöntemi var mı" sorusuna değil, "bu **tam tipin** yöntem kümesinde bu
yöntem var mı" sorusuna bakıyor. Derleyici bunu her `var _ error = ...` satırında, atamanın
yapıldığı **kullanım yerinde** denetliyor — kaynakta `ErisimHatasi implements error` gibi bir
bildirim hiç yer almadığı hâlde, denetim gerçek ve iki farklı sonuç üretiyor.

Bu ayrımın pratik bir sonucu var: bir tipin hata yöntemini işaretçi alıcıyla yazmak, o tipin
yalnızca işaretçi biçiminin hata sayılacağı anlamına geliyor. Bir işlev `ErisimHatasi{...}`
değerini doğrudan `error` olarak döndürmeye çalışırsa derleme burada durur; işlevin
`&ErisimHatasi{...}` döndürmesi gerekiyor. Bu seçim rastgele değil — büyük bir yapının her
döndürülüşünde kopyalanmasını önlemek isteyen bir tasarım, aynı zamanda o yapının yalnızca
işaretçi biçiminin hata arayüzüne girmesini de seçmiş oluyor.

#### Sınırlayıcı Ölçüm: Dolu Bir Arayüz nil Görünmez

Bir hata dönmeyen yolun döndürdüğü de bir `error` değeridir — genellikle `nil`. Ama bu `nil`in
nereden geldiği önemli. Aşağıdaki `dogrula` işlevi somut bir işaretçi tipi (`*AlanHatasi`)
döndürüyor ve alan doluysa bu işaretçi gerçekten `nil`. Sorun, bu somut değerin bir `error`
değişkenine konduğu satırda başlıyor — üstelik bu satır `kontrolEt` işlevinin dönüş ifadesi
kadar sıradan görünüyor.

Bu örnek, önceki iki bölümde ölçülen iki gözlemi tek bir satırda birleştiriyor: `AlanHatasi`nin
`Error` yöntemi işaretçi alıcılı, dolayısıyla yalnızca `*AlanHatasi` `error`i karşılıyor —
tıpkı `ErisimHatasi` gibi. Ve arayüz değeri, dinamik tip ile dinamik değeri birlikte taşıyan bir
çift — tıpkı ilk bölümde ölçülen üç tip gibi. Fark, bu kez dinamik değerin kendisinin `nil`
olması ve bunun arayüzün bütününü etkilememesi.

```go
// tuzak.go — nil isaretcili somut hata, error arayuzune konunca artik nil degil
package main

import "fmt"

type AlanHatasi struct{ Alan string }

func (a *AlanHatasi) Error() string { return "alan gecersiz: " + a.Alan }

func dogrula(alan string) *AlanHatasi {
	if alan == "" {
		return &AlanHatasi{Alan: "zorunlu"}
	}
	return nil
}

func kontrolEt(alan string) error {
	return dogrula(alan)
}

func main() {
	somut := dogrula("ad")
	hata := kontrolEt("ad")

	gozle("somut isaretcinin kendisi", "deger", fmt.Sprint(somut == nil))
	gozle("ayni degeri tasiyan hata arayuzu", "deger", fmt.Sprint(hata == nil))
	gozle("hata arayuzunun dinamik tipi", "deger", fmt.Sprintf("%T", hata))
	ozet()
}
```

```
somut isaretcinin kendisi                deger     true
ayni degeri tasiyan hata arayuzu         deger     false
hata arayuzunun dinamik tipi             deger     *main.AlanHatasi
bag: 0 kullanim yeri, 3 deger
```

**HT5.** `dogrula("ad")` çağrısı gerçekten `nil` bir işaretçi döndürüyor — üçüncü satır bunu
doğruluyor. Ama `kontrolEt`, bu işaretçiyi `error` tipinde bir değişkene **döndürdüğü** anda,
arayüz değeri iki parçalı hâle geliyor: dinamik tip (`*AlanHatasi`) ve dinamik değer (`nil`
işaretçi). Karşılaştırma `hata == nil`, arayüzün **kendisinin** boş olup olmadığına bakıyor;
dinamik tip dolu olduğu için arayüz de dolu sayılıyor, taşıdığı işaretçi `nil` olsa bile.

**HT6.** Bu, konunun ortak ölçütünün hata değerlerindeki en tuhaf karşılığı: on altı bağın
sekizi değerin içinde taşınıyordu ve bu satır tam olarak o sekizin neden bedelsiz olmadığını
gösteriyor. Bağın bozulduğu yer ne derleme, ne çağrı anı — yalnız değere `== nil` diye
sorulduğunda ortaya çıkıyor. Pratik sonucu açık: bir işlev `error` döndürecekse imzasında somut
bir işaretçi tipi değil, doğrudan `error` arayüzünü kullanmalı; aksi hâlde `dogrula` gibi bir
işlevin `nil` dönüşü, çağıranın elinde dolu bir hataya dönüşebiliyor.

Bu tuzağın sinsiliği, hatanın **gizli** kalmasında değil, tam tersinde: `hata != nil` denetimi
gayet dürüst bir biçimde çalışıyor ve doğru yanıtı veriyor — arayüz gerçekten dolu, çünkü
dinamik tipi taşıyor. Yanılan, kodu yazan kişinin beklentisi: `dogrula`nın `nil` döndürdüğü her
durumda `kontrolEt`in de `nil` döndüreceğini varsaymak. Bu varsayım, işaretçinin kendisiyle
işaretçiyi taşıyan arayüzün aynı şey olduğunu düşünmekten kaynaklanıyor; hâlbuki üçüncü satırın
gösterdiği gibi ikisi farklı sorulara yanıt veriyor.

Bu ders boyunca ölçülen dört gözlem tek bir çerçeveye oturuyor. Örtük karşılama bağı kaynakta
hiç yazılmıyor ve bunun yerini iki farklı mekanizma dolduruyor: derleyicinin bir atama satırında
denetlediği yöntem kümesi uyumu (kullanım yeri) ve arayüz değerinin kendi içinde taşıdığı
dinamik tip ile dinamik değer (değer). Bu ikisi birbirinden bağımsız değil — kullanım yerinde
kurulan bağ, sonradan değerin içinde taşınan çiftin **neyi** taşıyacağını belirliyor. `nil`
tuzağı da tam bu geçişte doğuyor: uyum bir kere kurulduktan sonra, taşınan değerin kendisi
`nil` olsa bile arayüz artık dolu.

#### Özet

- `error`, tek yöntemli sıradan bir arayüzdür (`Error() string`); bir tipin onu karşıladığı
  kaynağın hiçbir yerinde yazmaz, yalnız yöntemin varlığıyla kurulur.
- Değer alıcılı bir hata yapısal karşılaştırılır (alanlar eşitse değer eşittir); işaretçi
  alıcılı bir hata kimlik karşılaştırılır (adresler eşit değilse değer eşit değildir).
- `%T` ile okunan dinamik tip, bir hata değerinin arayüzü hangi somut tiple karşıladığını
  gösterir; bu bilgi kaynakta değil, değerin kendisinde durur.
- Somut bir işaretçi tipi `nil` olsa bile, o işaretçi bir `error` değişkenine konduğunda arayüz
  değeri dolu sayılır — dinamik tip taşındığı için karşılaştırma `false` döner.
- Bu davranış, `error` döndürecek işlevlerin imzasında somut bir tip değil doğrudan `error`
  arayüzünü kullanmasının gerekçesidir.
- Bağ iki yerde kuruluyor, hiçbirinde bildirimde değil: yöntem kümesi uyumu derleyicinin
  atama satırında denetlediği kullanım yeri, dinamik tip ile dinamik değerin çifti ise
  değerin kendisi.

#### Sonraki Adım

Bu derste ölçülen iki hata (`BicimHatasi`, `ErisimHatasi`) birer birer yazıldı ve her çağrıda
yeniden üretildi; hiçbiri paket düzeyinde tek bir değer olarak paylaşılmadı. Ama bir çağıran,
belirli bir hata türünü **tanımak** istediğinde — "bu, tam olarak şu hata mı?" diye sormak
istediğinde — hatanın metnine değil kimliğine güvenmesi gerekiyor. Bu derste ölçülen değer
alıcılı ve işaretçi alıcılı hataların karşılaştırma farkı, tam olarak bu sorunun zeminini
hazırlıyor: bir kimliği güvenilir kılmak için karşılaştırmanın yapısal değil kimlik tabanlı
olması gerekiyor. Nasıl ki bu derste `error` arayüzünü karşılayan tipin hiç `error` sözcüğünü anmadığı ölçüldüyse,
sıradaki ders bu kimliğin
nerede durduğunu ölçüyor: paket düzeyinde bir kez tanımlanan bir hata değeri, aynı metni taşıyan
başka bir hatadan neden hâlâ ayırt edilebiliyor?

### Nöbetçi Hatalar

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/nobetci-hatalar

Paket düzeyinde bir kez tanımlanan bir hata değerinin kimliği metninde değil kendisinde durur: aynı metni taşıyan başka bir errors.New değeri onunla karşılaştırıldığında eşit çıkmaz, çünkü ikisi de *errors.errorString tipinde farklı iki değerdir. errors.Is, == ile aynı sonucu verir. Sınırlayıcı ölçüm: nöbetçi hatanın kimliği değiştiğinde çağıran taraf sessizce yanlış dala düşer, hiçbir uyarı üretilmez.

Önceki ders, değer alıcılı bir hatanın yapısal, işaretçi alıcılı bir hatanın kimlikle
karşılaştırıldığını ölçtü. Bu ders o farkı bir adım ileri taşıyor: bir çağıran, belirli bir
hatayı **tanımak** istediğinde neye güveniyor? Yanıt Go'da yaygın bir kalıba dayanıyor —
**nöbetçi hata**: paket düzeyinde bir kez tanımlanan, dışa aktarılan bir hata değeri. Bu dersin
sorusu şu: bu değerin kimliği nerede duruyor ve aynı metni taşıyan başka bir hata onunla neden
karıştırılmıyor?

Bu isim, Algoritmalar kursundaki **nöbetçi değişkeni** ve Python kursundaki **nöbetçi değer** ile
aynı aileden geliyor: orada bir dizinin sonunu ya da özel bir konumu işaretleyen tek bir değer,
burada bir başarısızlık **durumunu** işaretliyor. Kavram tekrar kurulmuyor; ortak olan yalnızca
fikir — bir arama ya da denetim döngüsünü, her adımda ayrı bir kural yazmak yerine tek bir
karşılaştırmaya indirgeyen özel bir değer.

#### Aynı Metin, Ayrı Kimlik

`errors.New`, çağrıldığı her seferde yeni bir değer üretir. Aşağıdaki `araBirinci` paket
düzeyinde tanımlanan `ErrBulunamadi` nöbetçisini döndürüyor; `araIkinci` ise aynı metni taşıyan
ama `errors.New` ile **ayrıca** üretilmiş bir değer döndürüyor. İkisi de kullanıcıya aynı
şeyi söylüyormuş gibi görünüyor.

Bu ikisi gerçek kodda genellikle iki ayrı paketten geliyor: bir depo katmanı `ErrBulunamadi` gibi
bir nöbetçi tanımlar; çağıran bu nöbetçiyle karşılaştırarak "kayıt yoktu" durumunu özel olarak ele
alır. Sorun, bir başka katmanın — belki aynı anlamı taşımak isteyen bir üçüncü paketin — kendi
`errors.New("kayit bulunamadi")` çağrısını yazmasıyla başlıyor; metin okuyucuya aynı görünse de,
iki değer birbirinden habersiz iki ayrı kimlik taşıyor.

```go
// bag.go — hata bagi kullanim yerinde mi kuruluyor, degerin icinde mi
package main

import "fmt"

var kullanimYeri, deger int

func gozle(ad, yer, kanit string) {
	switch yer {
	case "kullanim":
		kullanimYeri++
	default:
		deger++
	}
	fmt.Printf("%-42s %-9s %s\n", ad, yer, kanit)
}

func ozet() {
	fmt.Printf("bag: %d kullanim yeri, %d deger\n", kullanimYeri, deger)
}
```

```go
// nobetci.go — paket duzeyinde tanimlanan bir hata degeri, ayni metinli baska bir degerden kimlikle ayrilir
package main

import (
	"errors"
	"fmt"
)

var ErrBulunamadi = errors.New("kayit bulunamadi")

func araBirinci(id int) error {
	if id != 1 {
		return ErrBulunamadi
	}
	return nil
}

func araIkinci(id int) error {
	if id != 1 {
		return errors.New("kayit bulunamadi")
	}
	return nil
}

func main() {
	h1 := araBirinci(9)
	h2 := araIkinci(9)

	gozle("iki degerin dinamik tipi", "deger", fmt.Sprintf("%T, %T", h1, h2))
	gozle("nobetciyle == karsilastirmasi", "deger", fmt.Sprint(h1 == ErrBulunamadi))
	gozle("nobetciyle errors.Is karsilastirmasi", "deger", fmt.Sprint(errors.Is(h1, ErrBulunamadi)))
	gozle("ayni metinli farkli degerin == sonucu", "deger", fmt.Sprint(h2 == ErrBulunamadi))
	gozle("ayni metinli farkli degerin errors.Is sonucu", "deger", fmt.Sprint(errors.Is(h2, ErrBulunamadi)))
	gozle("iki farkli degerin metin karsilastirmasi", "deger", fmt.Sprint(h1.Error() == h2.Error()))
	ozet()
}
```

```
iki degerin dinamik tipi                   deger     *errors.errorString, *errors.errorString
nobetciyle == karsilastirmasi              deger     true
nobetciyle errors.Is karsilastirmasi       deger     true
ayni metinli farkli degerin == sonucu      deger     false
ayni metinli farkli degerin errors.Is sonucu deger     false
iki farkli degerin metin karsilastirmasi   deger     true
bag: 0 kullanim yeri, 6 deger
```

**HT7.** İlk satır önceki dersin dinamik tip ölçümünü burada tekrarlıyor ve şaşırtıcı bir
sonuç veriyor: `h1` ve `h2` **aynı** dinamik tipi taşıyor, `*errors.errorString` — `errors.New`
standart kitaplıkta bu tek tipin bir işaretçisini döndürüyor, hangi metni verirsen ver. Yani
aynı tip olmak, aynı değer olmak anlamına gelmiyor. `h1 == ErrBulunamadi` `true` dönüyor çünkü
`araBirinci` gerçekten paket düzeyindeki aynı değişkeni döndürdü — aynı adresi taşıyan aynı
işaretçi. `h2 == ErrBulunamadi` ise `false` dönüyor, çünkü `araIkinci` kendi `errors.New`
çağrısıyla **yeni bir değer** üretti; metin birebir aynı olsa da bu, farklı bir bellek
konumunda duran farklı bir yapı demek.

**HT8.** `errors.Is(h1, ErrBulunamadi)` ve `errors.Is(h2, ErrBulunamadi)`, sarmalanmamış bu iki
hatada `==` ile birebir aynı sonucu veriyor. Bunun nedeni `errors.Is`in davranışı: hedef hata
sarmalanmadıysa, karşılaştırma doğrudan `==`e iniyor. Bu eşdeğerlik yalnız bu derste geçerli —
sıradaki ders bir hata sarmalandığında `==`in neden yetersiz kaldığını ve `errors.Is`in neden
zorunlu hâle geldiğini ölçüyor. Son satır ise metin karşılaştırmasının (`Error() == Error()`)
kimlik karşılaştırmasından tamamen ayrı bir soruya yanıt verdiğini gösteriyor: iki farklı
değer, metni birebir aynı olduğu için `true` dönüyor — kimlik sorusuna yanlış yanıt verecek
bir kısayol.

Bu ölçüm, nöbetçi hata kalıbının neden `const` değil `var` ile kurulduğunu da açıklıyor.
`error` bir arayüz olduğu için bir sabite bağlanamaz — sabitler yalnız temel tiplerde
(sayı, dizgi, bool) tanımlanabilir. Bir nöbetçi hatanın "sabit" gibi davranması, dilin bir
kısıtlamasından değil, paket düzeyinde bir kez oluşturulup bir daha hiç değiştirilmeyen tek bir
değişkene bütün paketin aynı işaretçiyle erişmesinden geliyor. Kimliğin korunması, bu tek
değişkenin **paylaşılmasına** bağlı; her çağıran kendi `errors.New` kopyasını üretirse, kimlik
karşılaştırması daha en baştan anlamını yitiriyor.

#### Özel Kimlik: `Is` Yöntemiyle Kurulan Karşılaştırma

`errors.Is`in bu derste ölçülen davranışı — sarmalanmamış bir hatada `==`e inmesi — tek
kuralı değil. `errors` paketi bir hataya, karşılaştırmayı kendi belirlediği bir yönteme
devretme imkânı veriyor: `Is(hedef error) bool` yazan bir tip, `errors.Is` tarafından
çağrıldığında işaretçi eşitliğine değil, kendi mantığına göre yanıt veriyor. Bu, kimliğin
her zaman "aynı adres" anlamına gelmediğini, bir tipin kendi kimlik tanımını yazabildiğini
gösteriyor.

```go
// kodhatasi.go — ozel Is yontemiyle kurulan bir kimlik: isaretci esitligi olmadan da kimlik tanimlanabilir
package main

import (
	"errors"
	"fmt"
)

type KodHatasi struct{ Kod int }

func (k *KodHatasi) Error() string { return fmt.Sprintf("hata kodu %d", k.Kod) }

func (k *KodHatasi) Is(hedef error) bool {
	h, tamam := hedef.(*KodHatasi)
	return tamam && h.Kod == k.Kod
}

func main() {
	a := &KodHatasi{Kod: 404}
	b := &KodHatasi{Kod: 404}
	c := &KodHatasi{Kod: 500}

	gozle("iki ayri isaretcinin == sonucu", "deger", fmt.Sprint(a == b))
	gozle("Is yontemiyle ayni kodun karsilastirmasi", "deger", fmt.Sprint(errors.Is(a, b)))
	gozle("Is yontemiyle farkli kodun karsilastirmasi", "deger", fmt.Sprint(errors.Is(a, c)))
	ozet()
}
```

```
iki ayri isaretcinin == sonucu             deger     false
Is yontemiyle ayni kodun karsilastirmasi   deger     true
Is yontemiyle farkli kodun karsilastirmasi deger     false
bag: 0 kullanim yeri, 3 deger
```

**HT9.** `a` ve `b`, `KodHatasi` tipinden iki ayrı işaretçi; `==` bunları adres bazında
karşılaştırıyor ve `false` dönüyor, tıpkı önceki dersteki `ErisimHatasi` örneğinde olduğu gibi.
Ama `errors.Is(a, b)` çağrıldığında sonuç `true` — çünkü `KodHatasi`, `Is(hedef error) bool`
yöntemini yazmış ve `errors.Is`, hedefi `a`nın `Is` yöntemine soruyor. `a.Is(b)`, `b`nin de bir
`*KodHatasi` olduğunu ve `Kod` alanının eşit olduğunu doğruluyor; adres hiç devreye girmiyor.

**HT10.** `errors.Is(a, c)` ise `false` dönüyor, çünkü `c`nin `Kod` alanı farklı — özel `Is`
yöntemi hâlâ çalışıyor, yalnız bu kez kendi mantığına göre "eşit değil" diyor. Bu, kimliğin
Go'da tek bir mekanizmaya bağlı olmadığını gösteriyor: paket düzeyinde paylaşılan bir
değişkenin adresi bir kimlik biçimiyse, bir tipin kendi yazdığı `Is` yöntemi de başka bir
kimlik biçimi. `errors.Is` ikisini de kabul ediyor — önce `==` dener, sonra hedefin ya da
kaynağın bir `Is` yöntemi olup olmadığına bakıyor. Sıradaki bölümdeki nöbetçi hata ölçümü ilk
biçime, yani paylaşılan değişkene dayanıyor.

Bu iki biçimin seçimi, hatanın taşıdığı bilgiye bağlı. `ErrBulunamadi` gibi hiçbir ek veri
taşımayan bir durumda tek bir paylaşılan değer yeterli — her çağıran aynı adrese bakabiliyor.
Ama `KodHatasi` gibi kendi içinde değişen bir veri (`Kod`) taşıyan bir hata türünde, olası her
kod için önceden ayrı bir paylaşılan değişken tanımlamak pratik değil; `Is` yöntemi, "aynı
tipten ve aynı koddan mı" sorusunu tek bir karşılaştırmaya indiriyor ve her kod için yeni bir
nöbetçi üretme ihtiyacını ortadan kaldırıyor.

#### Sınırlayıcı Ölçüm: Kimlik Bir API Sözüdür

Bir nöbetçi hata dışa aktarıldığında (`ErrBulunamadi` gibi büyük harfle başlayan bir ad),
paketin kendisi bir söz veriyor: bu değer, bu paketten dönen belirli bir hata durumunu her
zaman **aynı işaretçiyle** temsil edecek. Bu söz bozulduğunda — aynı durumu farklı bir
`errors.New` çağrısıyla üreten bir değişiklik yapıldığında — kaynakta hiçbir uyarı çıkmıyor.

Bu söz genellikle kod tabanı büyüdükçe bozuluyor, kötü niyetten değil dikkatsizlikten. Bir
paketin ilk sürümü tek bir yerden `ErrYetkisiz` döndürürken, zamanla aynı durumu bildiren
ikinci bir işlev eklenir; yazan kişi paket düzeyindeki nöbetçiyi içe aktarmak yerine kendi
`errors.New` çağrısını yazarsa — belki dosyalar arasında geçiş yaparken, belki nöbetçinin
varlığından habersiz olduğu için — iki işlev artık aynı metni ama farklı kimliği taşıyan iki
ayrı hata üretir. Aşağıdaki ölçüm bu senaryoyu birebir kuruyor: `erisimA` nöbetçiyi
paylaşıyor, `erisimB` paylaşmıyor.

```go
// versiyon.go — nobetci hatanin kimligi degisirse cagiran sessizce yanlis dala duser
package main

import "errors"

var ErrYetkisiz = errors.New("yetkisiz erisim")

func erisimA(rutbe int) error {
	if rutbe < 2 {
		return ErrYetkisiz
	}
	return nil
}

func erisimB(rutbe int) error {
	if rutbe < 2 {
		return errors.New("yetkisiz erisim")
	}
	return nil
}

func isle(hata error) string {
	if hata == ErrYetkisiz {
		return "yetkisizlik dalina dusuldu, ozel islem yapildi"
	}
	if hata != nil {
		return "bilinmeyen hata dalina dusuldu, genel islem yapildi"
	}
	return "hata yok"
}

func main() {
	gozle("erisimA'nin nobetci hatasiyla isle", "deger", isle(erisimA(0)))
	gozle("erisimB'nin ayni metinli hatasiyla isle", "deger", isle(erisimB(0)))
	ozet()
}
```

```
erisimA'nin nobetci hatasiyla isle         deger     yetkisizlik dalina dusuldu, ozel islem yapildi
erisimB'nin ayni metinli hatasiyla isle    deger     bilinmeyen hata dalina dusuldu, genel islem yapildi
bag: 0 kullanim yeri, 2 deger
```

**HT11.** `erisimA` ve `erisimB`, kullanıcı için görünüşte aynı durumu bildiriyor — "yetkisiz
erişim" — ama yalnız `erisimA` gerçek `ErrYetkisiz` nöbetçisini döndürüyor. `isle` işlevi
`hata == ErrYetkisiz` ile özel bir dal ayırmış: `erisimA`nın hatası bu dala düşüyor, `erisimB`
ise düşmüyor ve genel dala kayıyor. Kaynağı okuyan biri için `erisimB`nin metni `erisimA`nınkiyle
birebir aynı olduğundan, bu ayrımın var olduğunu görmek mümkün değil — yalnız çalıştırıp
sonuçları karşılaştırmak gösteriyor.

**HT12.** Bu, nöbetçi hatanın kimliğinin neden bir **API sözü** sayıldığını gösteriyor:
`erisimB` gibi bir işlev, aynı durumu bildirdiğini düşünse de nöbetçiyi paylaşmadığı için sözü
tutmuyor. Derleme hiçbir şey söylemiyor, çünkü ikisi de geçerli bir `error` döndürüyor;
çalışma zamanında da hiçbir panik ya da hata mesajı çıkmıyor — `isle` yalnız beklenmeyen dala
sessizce düşüyor. Bir paketin bir nöbetçi hatayı bir kez yayımladıktan sonra, o hatayı üreten
her yol aynı değişkeni döndürmek zorunda; aksi hâlde çağıran tarafın kimlik karşılaştırması
gizlice yanlış sonuç vermeye başlıyor.

Bunun bir sınırı da var: kimlik yalnızca **aynı çalışan süreç** içinde anlamlı. `ErrYetkisiz`in
adresi, programın o çalıştırılışına özgü; bir hatayı diske yazıp sonra okumak, ya da bir ağ
çağrısıyla başka bir sürece taşımak, `error` değerini değil onun `Error()` metnini taşıyor —
kimlik bu geçişte kayboluyor. Bu yüzden nöbetçi hata karşılaştırması yalnızca aynı ikili
dosyanın (aynı derlemenin) içindeki çağrılar arasında güvenilir; sınır ötesinde (bir HTTP
yanıtında, bir günlük dosyasında) hata metninin ya da ayrı bir hata kodu alanının taşınması
gerekiyor.

Bu derste ölçülen üç bulgu birleştiğinde konunun ölçütü hata değerlerinde ilk kez tümüyle
**değer** sınıfına oturuyor. Örtük karşılama önceki dersin de gösterdiği gibi kullanım yerinde
kuruluyordu; ama bir hatayı **tanımak**, yani onu başka bir hatadan ayırt etmek, tamamen
değerin kendi kimliğine dayanıyor — ne kaynakta bir bildirim, ne derleyicinin baktığı bir
denetim var. Kimlik ya paylaşılan bir adrese (`ErrBulunamadi`) ya da tipin kendi yazdığı bir
`Is` yöntemine dayanıyor; ikisi de yalnızca çalışma zamanında, değere sorularak öğreniliyor.

#### Özet

- Bir nöbetçi hata, paket düzeyinde bir kez tanımlanan ve dışa aktarılan bir `error`
  değeridir; kimliği metninde değil, kendi değerinde durur.
- `errors.New`in her çağrısı yeni bir `*errors.errorString` değeri üretir — aynı metin bile
  versin, iki çağrı iki farklı değer, dolayısıyla iki farklı kimlik demektir.
- Sarmalanmamış bir hatada `errors.Is` ile `==` aynı sonucu verir; bu eşdeğerlik yalnız hata
  hiç sarmalanmadığında geçerlidir.
- Metin karşılaştırması (`Error() == Error()`) kimlik karşılaştırmasının yerini tutmaz: farklı
  kimlikli iki hata birebir aynı metni taşıyabilir.
- Nöbetçi hatanın kimliği bir API sözüdür; bir durumu bildiren her yol aynı paylaşılan
  değişkeni döndürmezse, çağıranın kimlik denetimi hiçbir uyarı vermeden yanlış sonuç üretir.
- Kimlik yalnız paylaşılan bir adrese değil, bir tipin kendi yazdığı `Is(hedef error) bool`
  yöntemine de dayanabilir; `errors.Is` ikisini de tanır, `==` yalnız birincisini. Kimlik
  yalnızca aynı çalışan süreç içinde geçerlidir, bir sınırı (diske yazma, ağ üzerinden
  taşıma) aşarken kaybolur.

#### Sonraki Adım

Bu derste ölçülen hatalar hep tek katmanlıydı: bir işlev nöbetçiyi ya doğrudan döndürdü ya da
onun yerine yeni bir değer üretti. Ama gerçek çağrı zincirleri genellikle birden çok katmandan
geçiyor ve her katman kendi bağlamını eklemek istiyor — "şu işlemi yaparken böyle bir hata
oluştu" gibi. Bir hata bu şekilde sarmalandığında, `h1 == ErrBulunamadi` artık işe yaramıyor;
dıştaki değer artık nöbetçinin kendisi değil, onu içinde taşıyan başka bir değer. `errors.Is`in
bu derste sarmalanmamış hatalarda `==`e indiği gözlemi de burada sona eriyor: sıradaki derste
zincir eklendiğinde, `errors.Is`in neden artık `==`den farklı bir sonuç ürettiği ve zincirin
dibindeki nöbetçinin kaç katman sonra hâlâ bulunabildiği ölçülüyor. Aynı soru, sarmalayan
metnin kendisi için de sorulacak: bir katmanın eklediği bağlam, zincirin geri kalanında nasıl
korunuyor?

### Hata Sarmalama ve Çözme

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/hata-sarmalama-ve-cozme

%w ile sarmalanan bir hata, dış hatanın içinde bir zincir olarak durur ve errors.Is bu zinciri Unwrap ile katman katman gezerek nöbetçiyi bulur; %v kullanıldığında aynı metin üretilse de zincir kopuyor ve errors.Is artık bulamıyor. errors.As zincirden belirli bir hedef tipi çıkarır. Sınırlayıcı ölçüm: sarmalayan bağlamın metni yanlış olsa bile zincirin kimliği bozulmuyor, hiçbir uyarı üretilmiyor.

Önceki ders, nöbetçi hatanın kimliğinin `==` ile ya da bir `Is` yöntemiyle nasıl korunduğunu
ölçtü — ama hep tek katmanlı çağrılarda. Gerçek çağrı zincirleri genellikle birden çok katmandan
geçiyor ve her katman kendi bağlamını eklemek istiyor: "şu işlemi yaparken böyle bir hata
oluştu" gibi. Bu ders bu bağlamın nasıl eklendiğini ve zincirin dibindeki nöbetçinin kaç katman
sonra hâlâ bulunabildiğini ölçüyor.

Sarmalama, aynı zamanda konunun ölçütü için de yeni bir gözlem sunuyor. Önceki ders bir
hatanın kimliğinin nasıl paylaşıldığını ölçtü; ama bir çağrı zinciri büyüdükçe, dıştaki hata
artık nöbetçinin **kendisi** değil, onu içeren başka bir değer oluyor. `h1 == ErrYok` gibi bir
karşılaştırma böyle bir zincirde işe yaramıyor — ne dıştaki değer nöbetçiyle aynı adresi
taşıyor, ne de metni aynı kalıyor. Bu ders, kimliğin bir zincire gömüldüğünde nasıl hâlâ
sorgulanabildiğini gösteriyor.

#### Zincirin Dibi: `%w` ile Taşınan Kimlik

`fmt.Errorf`, `%w` biçim fiiliyle çağrıldığında yeni bir hata üretiyor ve bu yeni hata, verilen
hatayı **içinde** tutuyor. Aşağıdaki üç katmanlı zincir bunu iki biçimde kuruyor: `Saglam` sürümü
her katmanda `%w` kullanıyor, `Kirik` sürümü ise ortada bir katmanda `%v` kullanıyor — ikisi de
aynı metni üretiyor.

Üç katman burada bilerek seçildi: gerçek bir kod tabanında bir depo katmanı, onu çağıran bir
servis katmanı ve o servisi çağıran bir işleyici katmanı benzer bir zincir kurar. Her katman
kendi bakış açısından bir cümle ekler ("kayıt bulunamadı" değil "kullanıcı doğrulanamadı: kayıt
bulunamadı" gibi), ama en dipteki nedenin ne olduğu sorusu değişmiyor. Ölçüm, bu sorunun
yanıtının zincir boyunca **korunup korunmadığını** sınıyor.

```go
// bag.go — hata bagi kullanim yerinde mi kuruluyor, degerin icinde mi
package main

import "fmt"

var kullanimYeri, deger int

func gozle(ad, yer, kanit string) {
	switch yer {
	case "kullanim":
		kullanimYeri++
	default:
		deger++
	}
	fmt.Printf("%-46s %-9s %s\n", ad, yer, kanit)
}

func ozet() {
	fmt.Printf("bag: %d kullanim yeri, %d deger\n", kullanimYeri, deger)
}
```

```go
// zincir.go — uc katmanli sarmalama: nobetci hata zincirin dibinde bile bulunuyor, %v zinciri kesiyor
package main

import (
	"errors"
	"fmt"
)

var ErrYok = errors.New("kayit yok")

func katman1() error {
	return ErrYok
}

func katman2Saglam() error {
	if hata := katman1(); hata != nil {
		return fmt.Errorf("katman2 basarisiz: %w", hata)
	}
	return nil
}

func katman3Saglam() error {
	if hata := katman2Saglam(); hata != nil {
		return fmt.Errorf("katman3 basarisiz: %w", hata)
	}
	return nil
}

func katman2Kirik() error {
	if hata := katman1(); hata != nil {
		return fmt.Errorf("katman2 basarisiz: %v", hata)
	}
	return nil
}

func katman3Kirik() error {
	if hata := katman2Kirik(); hata != nil {
		return fmt.Errorf("katman3 basarisiz: %w", hata)
	}
	return nil
}

func main() {
	saglam := katman3Saglam()
	kirik := katman3Kirik()

	gozle("saglam zincirin disaridaki metni", "deger", saglam.Error())
	gozle("kirik zincirin disaridaki metni", "deger", kirik.Error())
	gozle("saglam zincirde nobetci bulunuyor mu", "deger", fmt.Sprint(errors.Is(saglam, ErrYok)))
	gozle("kirik zincirde nobetci bulunuyor mu", "deger", fmt.Sprint(errors.Is(kirik, ErrYok)))
	ozet()
}
```

```
saglam zincirin disaridaki metni               deger     katman3 basarisiz: katman2 basarisiz: kayit yok
kirik zincirin disaridaki metni                deger     katman3 basarisiz: katman2 basarisiz: kayit yok
saglam zincirde nobetci bulunuyor mu           deger     true
kirik zincirde nobetci bulunuyor mu            deger     false
bag: 0 kullanim yeri, 4 deger
```

**HT13.** İlk iki satır kursun en net gösterimlerinden biri: iki zincirin **metni birebir
aynı**, çünkü `%w` ve `%v` ikisi de aynı `Error() string` çıktısını `fmt.Errorf`in ürettiği
yeni metnin içine yerleştiriyor. Metne bakan biri iki hatayı ayırt edemez. Ama üçüncü ve
dördüncü satır bambaşka bir sonuç veriyor: `errors.Is(saglam, ErrYok)` `true`, `errors.Is(kirik,
ErrYok)` `false`. Metin aynı kalırken sorgulama sonucu değişiyor — çünkü `errors.Is`in
sorduğu soru metne değil zincire bakıyor.

**HT14.** Fark, `%w` ile `%v` arasındaki tek bir davranışta: `%w`, `fmt.Errorf`in ürettiği yeni
hataya bir `Unwrap() error` yöntemi kazandırıyor ve bu yöntem verilen hatayı geri döndürüyor;
`%v` yalnızca metni gömüyor, hiçbir yöntem eklemiyor. `katman2Kirik`in ürettiği hatanın
`Unwrap`i yok — zincir orada, `ErrYok`e ulaşamadan kesiliyor. `katman3Kirik`in kendi `%w`
kullanması bu kesintiyi telafi etmiyor, çünkü zaten kestiği yer bir katman altında.

Bu, hata sarmalamanın **tek yönlü** bir zincir olduğunu gösteriyor: bir üst katman, altındaki bir
katmanın zinciri kırmasını düzeltemiyor. `katman3Kirik`, `katman2Kirik`in ürettiği hatayı `%w`
ile sarmalıyor ve kendi Unwrap yöntemini doğru kuruyor — ama sarmaladığı değerin **kendisi**
zaten dipteki `ErrYok`e bir bağ taşımıyor. Zincirin sağlamlığı, en zayıf katmanına bağlı;
tek bir `%v` kullanımı, üstündeki bütün katmanların doğru davranmasını anlamsız kılıyor.

#### Zincirin İçinde Gezinme: `errors.Unwrap`

`errors.Is`in yaptığı iş aslında tek bir ilkel üzerine kurulu: `errors.Unwrap`. Bu işlev bir
hatayı alıp, o hata bir `Unwrap() error` yöntemi yazmışsa bir alt katmanı döndürüyor; yazmamışsa
`nil` döndürüyor. `errors.Is`, hedefi bulana ya da `nil`e varana kadar bunu döngüyle çağırıyor.
Aşağıdaki ölçüm bu döngüyü elle kuruyor.

Bu, `errors.Is`in "sihirli" bir davranış olmadığını, sıradan bir döngü ve sıradan bir yöntem
çağrısı üzerine kurulduğunu gösteriyor. `%w` bir hataya `Unwrap` yöntemi kazandırdığı için zincir
kurulabiliyor; bu yöntem olmasaydı, `errors.Unwrap` ilk çağrıda `nil` dönerdi ve zincirin dibine
hiç inilemezdi. Önceki bölümdeki `katman2Kirik`in ürettiği hata tam olarak bu durumda: `%v` ile
üretildiği için `Unwrap` yöntemi yok; `errors.Unwrap` onu çağırdığında `nil` dönüyor.

```go
// adimadim.go — errors.Is'in gezdigi zinciri Unwrap ile elle adim adim izleme
package main

import (
	"errors"
	"fmt"
)

var ErrYok = errors.New("kayit yok")

func main() {
	sarmali := fmt.Errorf("dis katman: %w", fmt.Errorf("orta katman: %w", ErrYok))

	adim := 0
	guncel := sarmali
	for guncel != nil {
		gozle(fmt.Sprintf("zincirin %d. adimi", adim), "deger", guncel.Error())
		guncel = errors.Unwrap(guncel)
		adim++
	}
	gozle("errors.Is ayni zinciri tek cagrida bulur mu", "deger", fmt.Sprint(errors.Is(sarmali, ErrYok)))
	ozet()
}
```

```
zincirin 0. adimi                              deger     dis katman: orta katman: kayit yok
zincirin 1. adimi                              deger     orta katman: kayit yok
zincirin 2. adimi                              deger     kayit yok
errors.Is ayni zinciri tek cagrida bulur mu    deger     true
bag: 0 kullanim yeri, 4 deger
```

**HT15.** Döngü üç adımda duruyor: `guncel` sırasıyla dış hatayı, orta hatayı ve en dipteki
`ErrYok`i taşıyor; dördüncü çağrıda `errors.Unwrap(ErrYok)` `nil` dönüyor çünkü `ErrYok`
`errors.New` ile üretildi ve hiçbir `Unwrap` yöntemi yok — döngü orada duruyor. Her adımın
metni bir öncekinin **son parçası**; bu, zincirin gerçekten iç içe durduğunu, tek bir uzun
dizgi olarak değil, birbirine bağlı ayrı hata değerleri olarak taşındığını gösteriyor.
`errors.Is(sarmali, ErrYok)` bu üç adımı tek bir çağrıda yapıyor ve `true` dönüyor — önceki
bölümdeki `saglam` zinciriyle aynı mekanizma.

Bu adım adım gezinme, `errors.Is`in maliyetini de gösteriyor: zincir ne kadar uzunsa,
`errors.Unwrap` o kadar çok kez çağrılıyor. Pratikte bu maliyet, birkaç katmanlık bir çağrı
zincirinde göz ardı edilebilir düzeyde kalıyor — ama zincirin sonsuz döngüye girmemesi de
`Unwrap`in her adımda gerçekten **farklı** bir değer döndürmesine bağlı. Bir hatanın kendi
kendisini sarmalaması (`Unwrap`in aynı değeri döndürmesi) burada ölçülmüyor, ama zincirin
neden hep **ilerleyen**, hiç geri dönmeyen bir yapı olarak tasarlandığını açıklıyor.

#### Zincirden Tip Çıkarma: `errors.As`

`errors.Is` bir hedef **değerin** zincirde olup olmadığını soruyor; `errors.As` ise zincirde
belirli bir **tipte** bir hata olup olmadığını soruyor ve varsa onu bir değişkene yazıyor.
Bu, zincirin yalnız kimlik değil, taşıdığı **veriyi** de koruduğunu gösteriyor.

Bu ayrım, önceki dersteki iki kimlik biçimiyle de örtüşüyor. `ErrYok` gibi veri taşımayan bir
nöbetçi için soru hep "bu değer mi" — `errors.Is` yeterli. Ama `AlanHatasi` gibi kendi alanı
olan bir hata türünde asıl soru genellikle "bu türden bir hata mı ve alanı ne" — `errors.As`
bu soruyu yanıtlıyor. `errors.As`in ikinci argümanı bir işaretçiye işaretçi almasının nedeni
de bu: bulduğu değeri doğrudan çağıranın değişkenine yazabilmesi gerekiyor.

```go
// hedef.go — zincirden hedef tipi cikarma; yanlis baglam zincirin kimligini bozmuyor
package main

import (
	"errors"
	"fmt"
)

var ErrYok = errors.New("kayit yok")

type AlanHatasi struct{ Alan string }

func (a *AlanHatasi) Error() string { return "alan gecersiz: " + a.Alan }

func dogrula() error {
	return fmt.Errorf("kayit dogrulanamadi: %w", &AlanHatasi{Alan: "ad"})
}

func katman1() error {
	return ErrYok
}

func katmanYanlisBaglam() error {
	if hata := katman1(); hata != nil {
		return fmt.Errorf("beklenmeyen ag hatasi: %w", hata)
	}
	return nil
}

func main() {
	var hedef *AlanHatasi
	bulunduMu := errors.As(dogrula(), &hedef)
	gozle("zincirden AlanHatasi tipini cikarma", "deger", fmt.Sprint(bulunduMu, " ", hedef.Alan))

	yanlis := katmanYanlisBaglam()
	gozle("yanlis baglamla sarmalanan hatanin metni", "deger", yanlis.Error())
	gozle("yanlis baglama ragmen kimlik hala bulunuyor mu", "deger", fmt.Sprint(errors.Is(yanlis, ErrYok)))
	ozet()
}
```

```
zincirden AlanHatasi tipini cikarma            deger     true ad
yanlis baglamla sarmalanan hatanin metni       deger     beklenmeyen ag hatasi: kayit yok
yanlis baglama ragmen kimlik hala bulunuyor mu deger     true
bag: 0 kullanim yeri, 3 deger
```

**HT16.** `dogrula`, zincirin tepesine bir `*AlanHatasi` sarmalıyor. `errors.As(dogrula(),
&hedef)`, zinciri `errors.Unwrap` ile gezip `*AlanHatasi` tipinde bir hata arıyor, buluyor ve
`hedef`e **atıyor** — sonrasında `hedef.Alan` doğrudan okunabiliyor. Bu, `errors.Is`in
veremeyeceği bir şey: `errors.Is` yalnız "bu değer zincirde var mı" diye soruyor; `errors.As` "bu
tipte bir değer var mı, varsa onu bana ver" diyor. İkisi de aynı `Unwrap` zincirini geziyor,
farklı bir soru soruyor.

`dogrula` işlevinin dönüş tipi burada da dikkat çekici: imzası yalnız `error` yazıyor,
`*AlanHatasi` değil — tıpkı Hata Arayüzü dersinde ölçülen tuzağın tersini yaparak. Somut tip
imzada görünmüyor, yalnız zincirin içine gömülüyor; çağıran onu bilmek istiyorsa `errors.As` ile
zinciri sorgulamak zorunda. İmzanın kendisi, zincirin içinde hangi tiplerin durduğu konusunda
hiçbir söz vermiyor.

**HT17.** İkinci ölçüm, dersin sınırlayıcı gözlemini kuruyor. `katmanYanlisBaglam`, gerçek
neden `ErrYok` olsa da metne "beklenmeyen ag hatasi" yazıyor — bağlam yanlış, hatta yanıltıcı.
Ama `errors.Is(yanlis, ErrYok)` yine de `true` dönüyor: zincir hâlâ sağlam, çünkü `%w`
kullanıldı ve `Unwrap` yöntemi hâlâ `ErrYok`e ulaşıyor. Zincir **kimliği** korur, ama sarmalayan
metnin **doğruluğunu** hiç denetlemiyor — yanlış bir bağlam yazmak ne derlemede ne çalışma
zamanında hiçbir uyarı üretmiyor, çünkü `fmt.Errorf`in ilk argümanı sıradan bir dizgi ve
dilin ona bakan bir denetimi yok.

Bu gözlemin pratik sonucu açık: `errors.Is` ve `errors.As` ile yazılan bir denetim, sarmalayan
metnin ne söylediğine bakmadan doğru çalışmaya devam ediyor — bu bir avantaj, çünkü metin insan
okuması için serbestçe değiştirilebiliyor; kimlik denetimi bundan etkilenmiyor. Ama aynı özellik
bir risk de taşıyor: bir geliştirici zincire yanlış ya da yanıltıcı bir bağlam eklediğinde, bunu
fark etmenin tek yolu metni **okumak**; ne `errors.Is` ne `errors.As` bir uyarı veriyor, çünkü
ikisi de yalnızca kimliğe ve tipe bakıyor.

Bu üç ölçüm konunun ortak ölçütüne aynı biçimde bağlanıyor: nöbetçi hatanın kimliği (önceki ders),
hata sarmalamanın taşıdığı metin ve sarmalı zincirden tip çıkarma — üçü de **değerin içinde**
taşınıyor, kaynakta hiçbir bildirimle görünmüyor. Bu, on altı bağın sekizinin değerde taşındığı
ortak ölçümün hata zincirlerindeki karşılığı.

Bu üç dersin (Hata Arayüzü, Nöbetçi Hatalar, Hata Sarmalama ve Çözme) birlikte kurduğu resim
şöyle: `error` bir arayüz olarak örtük karşılanıyor, karşılayan tip kaynakta hiç anılmıyor
(kullanım yeri); bir hatanın kimliği bir adrese ya da bir `Is` yöntemine dayanıyor (değer); ve
bu kimlik, katmanlar arasında sarmalanarak taşınsa bile, `Unwrap` zinciri sayesinde kayıp
gitmiyor (yine değer). Kaynağı okuyan biri, üç katmanlı bir zincirin dibinde hangi nöbetçinin
durduğunu görmek için ne bir bildirime ne bir imzaya bakabilir — yalnızca zinciri gerçekten
gezmek (ya da `errors.Is`e bu gezintiyi yaptırmak) yeterli oluyor.

#### Özet

- `%w`, `fmt.Errorf`in ürettiği hataya bir `Unwrap() error` yöntemi ekler; `%v` eklemez —
  metin aynı kalsa da zincir `%v` kullanılan katmanda kopar.
- `errors.Is`, `errors.Unwrap`i hedefi bulana ya da `nil`e varana kadar döngüyle çağırır;
  zincir üç katmansa üç adımda geziliyor.
- `errors.As`, zincirde belirli bir tipte bir hata arar ve bulduğunda onu bir değişkene yazar;
  `errors.Is`in veremeyeceği veriyi (örneğin `Alan` gibi bir alanı) böyle taşır.
- Zincirin kimliği (`errors.Is`, `errors.As`) korunur, ama sarmalayan metnin doğruluğu hiç
  denetlenmez — yanlış bir bağlam yazmak hiçbir uyarı üretmez.
- Nöbetçi hatanın kimliği, hata sarmalamanın taşıdığı metin ve zincirden tip çıkarma, üçü de
  değerin içinde taşınır; kaynakta hiçbir bildirim bu bağı göstermez.
- `errors.Unwrap`in sonsuz döngüye girmemesi, her katmanın altında farklı bir değer taşımasına
  bağlıdır; zincir hep ilerler, hiç kendi başına dönmez.

#### Sonraki Adım

Bu derste ölçülen her katman, kendi bağlamını sarmalayarak bir `error` **döndürdü** — dönüş
tipi hep aynı kaldı, imza değişmedi. Zincir ne kadar uzarsa uzasın, her işlevin imzasında hâlâ
tek bir sözleşme vardı: son değer bir `error`. Ama Go'da bir işlevin başarısız olmasının bir de
imzada hiç görünmeyen bir yolu var: panik. Bir işlev `panic` çağırdığında dönüş tipinde
hiçbir iz kalmıyor — imza `func F() int` ile `func F() int` arasında hiçbir fark yok, biri
her zaman bir tamsayı döndürürken öbürü çalışma zamanında çökebiliyor. Sıradaki ders bu çıkışı
ölçüyor: bir işlev hata döndürmek yerine panikliyorsa, bu seçim imzada nerede görünüyor ve
çağıran bunu önceden nasıl bilebilir?

### Panik ve Kurtarma

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/panik-ve-kurtarma

Bir işlev hata dönmek yerine panikleyebilir ve bu seçim imzada hiçbir iz bırakmaz: (int, error) döndüren ve yalnız int döndüren iki işlev, biri her zaman değer üretirken öbürü çalışma zamanında çökebilir. recover yalnız kendisini doğrudan çağıran ertelenmiş işlevde çalışır; kurtarılan bir panik hataya çevrilmezse bilgi tümüyle kaybolur. Yığıt izi basılmaz, yalnız panik=true/false ve kurtarılan değerin tipi ölçülür.

Önceki üç ders, bir işlevin başarısızlığını her zaman aynı sözleşmeyle bildirdiğini ölçtü:
son dönüş değeri bir `error`. Zincir ne kadar uzarsa uzasın, katman ne kadar bağlam eklerse
eklesin, imza hep aynı kaldı. Bu ders o sözleşmenin dışına çıkıyor: Go'da bir işlevin
başarısız olmasının imzada hiçbir iz bırakmayan bir yolu var — panik. Bu dersin sorusu, konunun
sorusunun en keskin biçimi: paniğin bağı hiç kurulmuyorsa, çağıran bunu önceden nasıl bilebilir?

Panik, dilin çalışma zamanının kendisinin de kullandığı bir mekanizma: bir dilim sınırının
dışına erişmek, bir sözlüğe yazmadan önce ilklendirilmemiş bir işaretçiden okumak, sıfıra
bölme yapmak — bunların hepsi çalışma zamanı tarafından üretilen birer panik. Bu ders yalnız
programcının kendi yazdığı `panic` çağrılarını ölçüyor, ama gözlem ikisi için de aynı: hiçbiri
imzada görünmüyor.

#### İmzada Görünen Kanal, Görünmeyen Çıkış

Aynı işi iki biçimde yazmak mümkün: bir `Depo` türünün `getir` işlevi ya `(int, error)`
döndürüp bulunamama durumunu ikinci değerde taşıyabilir, ya da yalnız `int` döndürüp
bulunamadığında panikleyebilir. Aşağıdaki ölçüm ikisini de üç katmanlı bir çağrı zincirinde
kuruyor — `ic`, onu çağıran `orta`, onu çağıran `dis`. Üç katman bilerek seçildi: Hata
Sarmalama ve Çözme dersindeki zincirle aynı derinlik, ama bu kez katmanlar hiçbir bağlam
eklemiyor, yalnızca çağrıyı iletiyor — ölçülen şey bağlamın taşınması değil, başarısızlık
kanalının kendisinin katmanlar boyunca ne kadar görünür kaldığı.

```go
// bag.go — panigin bagi bildirimde mi, degerin icinde mi, yoksa hicbir yerde mi kuruluyor
package main

import "fmt"

var bildirim, deger, yok int

func gozle(ad, yer, kanit string) {
	switch yer {
	case "bildirim":
		bildirim++
	case "deger":
		deger++
	default:
		yok++
	}
	fmt.Printf("%-58s %-9s %s\n", ad, yer, kanit)
}

func ozet() {
	fmt.Printf("bag: %d bildirim, %d deger, %d hicbir yerde\n", bildirim, deger, yok)
}
```

```go
// bicimler.go — ayni is iki bicimde: hata donen ve panikleyen, uc katmanli zincir
package main

import "fmt"

type Depo struct{ ogeler map[string]int }

func (d Depo) icHata(ad string) (int, error) {
	v, tamam := d.ogeler[ad]
	if !tamam {
		return 0, fmt.Errorf("bulunamadi: %s", ad)
	}
	return v, nil
}
func (d Depo) ortaHata(ad string) (int, error) { return d.icHata(ad) }
func (d Depo) disHata(ad string) (int, error)  { return d.ortaHata(ad) }

func (d Depo) icPanik(ad string) int {
	v, tamam := d.ogeler[ad]
	if !tamam {
		panic(fmt.Sprintf("bulunamadi: %s", ad))
	}
	return v
}
func (d Depo) ortaPanik(ad string) int { return d.icPanik(ad) }
func (d Depo) disPanik(ad string) int  { return d.ortaPanik(ad) }

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func main() {
	depo := Depo{ogeler: map[string]int{"a": 1}}

	sonuc, hata := depo.disHata("yok")
	gozle("hata donen imzanin gorunur kanali", "bildirim", "(int, error) -- ikinci deger hatayi tasiyor")
	gozle("uc katmandan gecen hata donen cagri", "deger", fmt.Sprint(sonuc, " ", hata))

	panikledi := panikliyorMu(func() { depo.disPanik("yok") })
	gozle("panikleyen imzanin gorunur kanali", "hicbir", "int -- ikinci bir kanal yok")
	gozle("uc katmandan gecen panikleyen cagri", "hicbir", fmt.Sprint("panik=", panikledi))

	ozet()
}
```

```
hata donen imzanin gorunur kanali                          bildirim  (int, error) -- ikinci deger hatayi tasiyor
uc katmandan gecen hata donen cagri                        deger     0 bulunamadi: yok
panikleyen imzanin gorunur kanali                          hicbir    int -- ikinci bir kanal yok
uc katmandan gecen panikleyen cagri                        hicbir    panik=true
bag: 1 bildirim, 1 deger, 2 hicbir yerde
```

**HT18.** `icHata`nın imzası `(int, error)` — başarısızlık kanalı doğrudan **bildirimde** duruyor;
`orta` ve `dis` bu ikinci değeri değiştirmeden taşıyor. `icPanik`in imzası ise yalnız `int`;
başarısızlık için ikinci bir yuva yok. `panikliyorMu` aracı (Fonksiyonlar dersinden ödünç alınan
`derlenirMi` gibi, K01'in araç setinden) burada da yalnız geçip geçmediğini değil, panikleyip
paniklemediğini gözlemliyor ve yığıt izi hiç basmıyor. Üçüncü katmandan geçen çağrı gerçekten
paniklemiş; ama bunu **görebilmek**, `panikliyorMu` gibi bir sarmalayıcı yazmayı gerektirdi —
sıradan bir çağrı (`depo.disPanik("yok")`) hiçbir işaret vermeden çöker.

**HT19.** Bu iki satır dersin merkezi gözlemini kuruyor: hata dönen biçimde başarısızlık
kanalı **imzada bir yer** kaplıyor (bildirim), panikleyen biçimde ise **hiçbir yerde**
kaplamıyor. Bu, konunun bildirim / kullanım yeri / değer üçlüsünün dışına çıkan dördüncü bir
durum — bağ ne kaynakta yazılı, ne derleyicinin bir atama noktasında denetlediği bir şey, ne
de değerin taşıdığı bir çift. `icPanik`i çağıran biri, imzaya bakarak bu işlevin başarısız
olabileceğini öğrenemiyor; bunu öğrenmenin tek yolu ya belgeleme (kaynağın dışında bir metin)
ya da işlevin gövdesini okumak.

Bu asimetrinin bir bedeli var, ama bedel iki yönde de işliyor. Hata dönen biçimde her katman
(`ic`, `orta`, `dis`) ikinci değeri elle taşımak zorunda — üç satırda üç kez `return ...,
err` yazmak, ya da en azından üç kez ikinci değeri fark etmek gerekiyor. Panikleyen biçimde
katmanlar hiçbir şey yazmıyor; sıradan bir fonksiyon çağrısı yeterli, çünkü panik kendiliğinden
yayılıyor. Görünürlüğün bedeli, her katmanda tekrar eden bir yazım; görünmezliğin bedeli de,
çağıranın hiçbir uyarı almadan kalması.

#### Kurtarma Yalnız Ertelenmiş Çağrıda Çalışır

`recover`, bir paniği durdurup normal yürütmeye dönmenin tek yolu — ama yalnız tek bir konumda
çalışıyor: `defer` ile ertelenmiş bir işlevin **doğrudan içinde**. `recover` çağrısı bir
yardımcı işleve taşındığında, o yardımcı ertelenmiş işlev tarafından çağrılıyor olsa bile,
`recover` artık hiçbir şey yakalamıyor.

Bu kısıtlama rastgele değil: `recover`in kendisi çalışma zamanına, çağrı yığıtının hangi
çerçevesinden çağrıldığını soruyor ve yalnız o çerçeve **paniklemekte olan işlevin doğrudan
ertelediği çerçeve** ise bir şey döndürüyor. Bu, `recover`i sıradan bir yardımcı işlevin
içine gömüp yeniden kullanılabilir kılma — "bir kere yaz, her yerde kullan" biçimindeki
kısayolların — neden çoğu zaman işe yaramadığını açıklıyor.

```go
// kurtar.go — recover yalniz kendisini dogrudan cagiran defer icinde calisiyor
package main

import "fmt"

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func dogrudanKurtar() (kurtarildiMi bool) {
	defer func() {
		if recover() != nil {
			kurtarildiMi = true
		}
	}()
	panic("test")
}

func dolayliKurtarYardimcisi() interface{} {
	return recover()
}

func dolayliKurtar() (kurtarildiMi bool) {
	defer func() {
		r := dolayliKurtarYardimcisi()
		if r != nil {
			kurtarildiMi = true
		}
	}()
	panic("test")
}

func main() {
	a := dogrudanKurtar()
	gozle("recover dogrudan defer icinde cagrildi", "deger", fmt.Sprint(a))

	disaKactiMi := panikliyorMu(func() { dolayliKurtar() })
	gozle("recover bir yardimci uzerinden cagrildi, disariya kacti mi", "deger", fmt.Sprint(disaKactiMi))
	ozet()
}
```

```
recover dogrudan defer icinde cagrildi                     deger     true
recover bir yardimci uzerinden cagrildi, disariya kacti mi deger     true
bag: 0 bildirim, 2 deger, 0 hicbir yerde
```

**HT20.** `dogrudanKurtar`, `recover()`i doğrudan `defer func() { ... }()` gövdesinde çağırıyor
ve paniği yakalıyor — `kurtarildiMi` `true` dönüyor. `dolayliKurtar` ise aynı şeyi yapmaya
çalışıyor gibi görünüyor: bir `defer` yazmış, o `defer` de bir işlev çağırıyor, o işlev de
`recover()`i çağırıyor. Ama arada bir çağrı katmanı olduğu için `recover`, kendisini çağıran
işlevin ertelenmiş **olmadığını** görüyor ve `nil` dönüyor — panik yakalanmıyor, `dolayliKurtar`
kendi dışına kaçıyor. Bu kaçışı görebilmek için onu bir `panikliyorMu` sarmalayıcısıyla
çağırmak gerekti; ikinci satır bu dış sarmalayıcının paniği yakaladığını gösteriyor.

**HT21.** Bu, `recover`in dilde en dar tanımlı yerlerden biri olduğunu gösteriyor: yalnızca "şu an
çalışan işlev, panikleyen çağrının doğrudan ertelediği işlev mi" sorusuna bakıyor. Çağrı derinliği
önemli değil — `dogrudanKurtar`in kendisi `panikliyorMu`nun içinde de çağrılabilirdi; sonuç
değişmezdi; önemli olan `recover`i çağıran işlevin **kendisinin** `defer` ile ertelenmiş olması.
Bu kural, `recover`i bir yardımcı işleve taşıyarak yeniden kullanılabilir kılma girişimlerinin
neden başarısız olduğunu açıklıyor.

#### Sınırlayıcı Ölçüm: Kurtarma Paniği Yok Etmez, Taşır

Bir panik kurtarıldığında, taşıdığı bilgi (paniğe verilen değer) kaybolmuyor — `recover()`
onu döndürüyor. Ama bu bilgiyi **kullanmak** çağıranın sorumluluğu. Aşağıdaki iki işlev aynı
paniği yakalıyor; biri onu bir `error`e çeviriyor, öbürü sessizce atıyor.

Bu iki işlev, panik ile hata arasındaki köprüyü de gösteriyor: bir panik kurtarıldıktan sonra,
sonucu tekrar Go'nun sıradan hata sözleşmesine — `(sonuc, error)` biçimine — sokmak tümüyle
mümkün. `calistirKurtararak`, bir kütüphanenin dış yüzeyinde panikleyen bir alt sistemi
saklayıp çağıranına sıradan bir `error` sunmak için sık kullanılan bir kalıbı örnekliyor.

```go
// kurtarma.go — kurtarma panigi yok etmiyor, tasiyor; hataya cevrilmezse bilgi kayboluyor
package main

import (
	"fmt"
	"strings"
)

func unluSayisi(kelime string) int {
	if kelime == "" {
		panic("bos kelime")
	}
	sayi := 0
	for _, r := range kelime {
		if strings.ContainsRune("aeiouAEIOU", r) {
			sayi++
		}
	}
	return sayi
}

func calistirKurtararak(f func() int) (sonuc int, hata error) {
	defer func() {
		if r := recover(); r != nil {
			hata = fmt.Errorf("kurtarilan panik (%T): %v", r, r)
		}
	}()
	return f(), nil
}

func calistirSessizce(f func() int) (sonuc int) {
	defer func() {
		recover()
	}()
	return f()
}

func main() {
	sonuc1, hata1 := calistirKurtararak(func() int { return unluSayisi("") })
	gozle("bos kelimenin kurtarilan panigi hataya cevrildi", "deger", fmt.Sprint(sonuc1, " ", hata1))

	sonucBos := calistirSessizce(func() int { return unluSayisi("") })
	sonucGercekSifir := calistirSessizce(func() int { return unluSayisi("krt") })
	gozle("bos kelimenin kurtarilan sonucu", "hicbir", fmt.Sprint(sonucBos))
	gozle("unlusuz gercek kelimenin sonucu", "hicbir", fmt.Sprint(sonucGercekSifir))

	ozet()
}
```

```
bos kelimenin kurtarilan panigi hataya cevrildi            deger     0 kurtarilan panik (string): bos kelime
bos kelimenin kurtarilan sonucu                            hicbir    0
unlusuz gercek kelimenin sonucu                            hicbir    0
bag: 0 bildirim, 1 deger, 2 hicbir yerde
```

**HT22.** `calistirKurtararak`, kurtardığı değeri `%T` ile tipiyle birlikte bir `error`e
çeviriyor — `unluSayisi("")` paniklediğinde verdiği değer bir dizgi (`string`) ve bu dönen
hatada görünüyor. Kurtarma, paniği **yok etmiyor**; taşıdığı bilgiyi bir başka biçimde, imzanın
zaten sahip olduğu bir kanala (`error`) aktarıyor. Bu satır, bir panik kurtarıldığında bile
`%T`nin hâlâ hangi değerin panikletildiğini gösterebildiğini kanıtlıyor.

**HT23.** `calistirSessizce` ise aynı paniği yakalıyor ama `recover()`in döndürdüğü değeri
hiçbir yere yazmıyor. Sonuç: `unluSayisi("")` panikleyip kurtarıldığında dönen `0` ile
`unluSayisi("krt")`nin (hiç ünlü içermeyen, gerçekten hesaplanmış bir dizgi) döndürdüğü `0`
birbirinden **ayırt edilemiyor**. Bu, dersin sınırlayıcı ölçümü: kurtarma bilgiyi taşıyor, ama
kurtarılan değer bir yere yazılmazsa, o taşınan bilgi çağıranın erişemeyeceği bir yerde
tümüyle kayboluyor — geriye "başarılı gibi görünen" bir sıfır kalıyor. Bu örnekte sıfırın
kendisi bile bir tuzak: `unluSayisi("krt")` gerçekten hesaplanmış, meşru bir sıfırdır; panik
kaynaklı sıfırla aralarında hiçbir işaret farkı yok. Genel olarak, kurtarma sonrası sessizce
bırakılan bir dönüş değeri, tipinin sıfır değeriyle **her zaman** karışabilir — bu, hangi tip
seçilirse seçilsin geçerli bir risk.

Bu üç ölçüm birlikte konunun sorusuna kesin bir yanıt veriyor: paniğin bağı bildirimde değil
(imzada hiçbir iz yok), kullanım yerinde değil (derleyicinin baktığı hiçbir atama noktası yok)
ve değerde de değil (çağıran, panikleme ihtimalini herhangi bir tipten okuyamıyor) — panik,
konunun üç yerinin **hiçbirinde** kurulmuyor. Tek kurulma noktası, kurtarma koduna kadar hiç
görünmeyen çalışma zamanı; ve kurtarma bile, bilgiyi başka bir kanala elle taşımadıkça hiçbir
şeyi telafi etmiyor.

#### Özet

- Hata dönen bir işlevin imzası başarısızlık için bir kanal ayırır (`bildirim`); panikleyen bir
  işlevin imzasında bu kanal hiç yoktur — panik konunun üç yerinin hiçbirinde kurulmaz.
- `recover`, yalnızca kendisini doğrudan çağıran ertelenmiş (`defer`) işlevin içinde çalışır;
  bir yardımcı işleve taşındığında `nil` döner ve panik yakalanmadan yayılmaya devam eder.
- Bir panik kurtarıldığında taşıdığı değer kaybolmaz; `%T` ile tipiyle birlikte okunabilir ve
  bir `error`e çevrilebilir.
- Kurtarılan değer bir yere yazılmazsa bilgi tümüyle kaybolur: gerçek bir sıfır sonuçla
  kurtarılmış bir paniğin sıfır sonucu ayırt edilemez hâle gelir.
- Yığıt izi, bellek adresi ve goroutine numarası hiçbir ölçümde basılmaz; yalnız `panik=true`/
  `panik=false` ve kurtarılan değerin `%T` ile okunan tipi kaydedilir.

#### Sonraki Adım

Bu konu tek bir soruyu dört kez sordu: `error` arayüzünü bir tip nasıl karşılıyor, bir hatanın
kimliği nerede duruyor, bir zincirin bağlamı nasıl korunuyor ve son olarak bir çıkışın bağı
hiç kurulmadığında ne oluyor. İlk üç ders, hep aynı yanıtı buldu: bağ **kullanım yerinde**
kuruluyor (bir değişkene atama, bir yöntem kümesi denetimi) ya da **değerin içinde** taşınıyor
(dinamik tip, kimlik, sarmalanmış zincir). Bu ders, bu ikisinin de dışında kalan dördüncü bir
durumu ölçtü: paniğin bağı hiçbir yerde kurulmuyor ve imzada hiç görünmüyor — çağıran, bir
işlevin panikleyebileceğini imzasından öğrenemiyor.

Sıradaki konu, genellikler, bunun tam tersini yapıyor. Bir genel fonksiyonun tip parametresi
de kaynakta hiç yazılmıyor — çağıran hiçbir tip belirtmeden bir işlevi çağırabiliyor. Ama
panikten farklı olarak bu bağı **derleyici** kuruyor ve tam olarak çağrı yerinde: her çağrı,
hangi tiple örneklendiğini derleme zamanında belirliyor. Aynı "kaynakta yazılmama" gözlemi,
iki konuda birbirinin tam tersi iki sonuca varıyor — panik her denetimi kaybediyor, genellik
her denetimi kullanım yerine taşıyor ve bağ hiçbir zaman bir değerin içine girmiyor.

## Genellikler

Tip parametresinin çağrı yerinde çıkarılması, tip kısıtlarının arayüz biçiminde bildirilmesi ve küme birleşimleri, tip güvenli kapsayıcıların örneklemesinin çağıranın bildiriminde durması, ve aynı işi arayüzle mi genellikle mi yapmanın ölçülebilir farkı.

### Genel Fonksiyonlar

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/genel-fonksiyonlar

Bir genel fonksiyonun tip parametresi kaynakta hiç yazılmıyor, ama derleyici onu çağrı yerinde çıkarımla kuruyor: aynı ilkOge kaynağı int, string ve float64 dilimleriyle üç kez çağrıldığında %T üç ayrı yanıt veriyor. Sınırlayıcı ölçüm, kısıtsız bir tip parametresinin gövdede neredeyse hiçbir işleme izin vermediğini gösteriyor: == ve + derlenmiyor, yalnız len gibi tipten bağımsız işlemler geçiyor.

Panik ve Kurtarma dersi bir bağın hiçbir yerde kurulmadığını ölçtü: paniğin var olduğu bilgisi
çağıranın imzasında görünmüyor; çağıran onu ancak koşum sırasında öğreniyordu. Bu konu tersini
yapıyor. Genel bir fonksiyonun tip parametresi de kaynakta hiç yazılmaz — `ilkOge[T any](d []T)`
çağrıldığında `T`nin ne olacağı hiçbir çağrı satırında görünmez — ama bağ ortada kalmıyor:
derleyici onu **çağrı yerinde**, argümanların tipine bakarak kurar ve kurduğu bağ hiçbir zaman
**değerin içine** girmez. Bu, kursun sorduğu soruyu bir kez daha sorar: bu bağ nerede kuruluyor?

Ortak ölçümde on altı bağın dördü bildirimde, dördü kullanım yerinde, sekizi değerin
içinde duruyor; bu dersin konusu olan tip parametresi çıkarımı, kullanım yerinde kurulan
dört bağdan biridir. Genellikler ile arayüzler aynı soruyu iki ayrı yerde yanıtlıyor —
arayüzde bağ değere girerken, genellikte hiç girmiyor — ve bu fark kursun üçüncü iddiasının
konusu; bu ders o farkın ilk yarısını, kullanım yerini, kurar.

Genellikler öncesinde aynı işi iki yoldan biriyle yazmak gerekiyordu: ya her tip için ayrı bir
fonksiyon (`ilkOgeInt`, `ilkOgeDizgi`, `ilkOgeOndalik`), ya da tek bir fonksiyon yazıp parametreyi
boş arayüzle (`any`) almak ve gövdede tip iddiasıyla somut tipe geri dönmek. Birincisi kaynağı
çoğaltıyor — aynı mantığın dört kopyası, dördü de bakım gerektiren ayrı bir yer. İkincisi kaynağı
tek tutuyor ama tip bilgisini derleme zamanından çalışma zamanına itiyor: çağıran hangi tipi
geçirirse geçirsin fonksiyon kabul ediyor; hatalı bir tip ancak koşum sırasında, bir tip iddiası
başarısız olduğunda ortaya çıkıyor. Genel fonksiyon üçüncü bir yol açıyor: kaynak tek, ama tip
denetimi derleme zamanında kalıyor — `ilkOge` bir `[]bool` ile çağrılırsa bu satır derlenmeden
önce reddedilir; `T` gövdede bir kez yazılır ve her çağrı için ayrıca kontrol edilir. Bu üç
yazımın aynı işi nerede bağladığı — bildirimde çoğaltarak, değerde geciktirerek, kullanım yerinde
derleyerek — kursun kapanış dersinin konusu; bu ders yalnız üçüncüsünü kurar.

#### Tip Parametresi Çağrı Yerinde Kuruluyor

Genel bir fonksiyon, adının hemen ardına köşeli parantezle bir **tip parametresi** listesi
ekler: `func ilkOge[T any](d []T) (T, bool)`. `T` burada somut bir tip değil, fonksiyon
çağrıldığında doldurulacak bir yer tutucudur; `any` ise bu yer tutucunun kabul ettiği
tiplerin sınırını söyler — şimdilik sınır yok, her tip geçerli. Fonksiyonun kendisi tek bir
kaynakta yazılıdır ve bu kaynak, hangi somut tiple **örnekleneceğinden** bağımsızdır.

**GN1.** Fonksiyon gövdesindeki `var sifir T` satırı, `T`nin ne olacağını hiç bilmeden
yazılmış bir sıfır değer üretimidir; bu satır kaynakta bir kez durur ve her çağrıda aynen
kalır — bir **bildirim**dir. `T`nin kendisi ise her çağrıda değişir ve bu değişimin
nerede olduğu kaynakta hiçbir yerde yazılı değildir; derleyici onu yalnız çağrı satırındaki
argümanın tipine bakarak kurar — bir **kullanım yeri** bağıdır. Aynı fonksiyonun iki
parçası, kursun iki ayrı sınıfına düşüyor.

```go
// cikarim.go — tip parametresi cagri yerinde cikariliyor, sifir deger mantigi bildirimde yaziyor
package main

import "fmt"

func gozle(ad, yer, kanit string) {
	fmt.Printf("%-42s %-10s %s\n", ad, yer, kanit)
}

// ilkOge her tip icin ayni kaynaktan calisir; sifir T satiri fonksiyonun kendi
// bildiriminde durur ve hangi somut tipin gelecegini hic bilmez.
func ilkOge[T any](dilim []T) (T, bool) {
	var sifir T
	if len(dilim) == 0 {
		return sifir, false
	}
	return dilim[0], true
}

func sifirDeger[T any]() T {
	var s T
	return s
}

func main() {
	fmt.Println("-- bag nerede kuruluyor: genel fonksiyon --")

	i, _ := ilkOge([]int{7, 2, 9})
	gozle("tip parametresi (tamsayi dilimi)", "kullanim", fmt.Sprintf("cikarildi, T=%T", i))

	d, _ := ilkOge([]string{"elma", "armut"})
	gozle("tip parametresi (dizgi dilimi)", "kullanim", fmt.Sprintf("cikarildi, T=%T", d))

	o, _ := ilkOge([]float64{1.5, 2.5})
	gozle("tip parametresi (ondalik dilimi)", "kullanim", fmt.Sprintf("cikarildi, T=%T", o))

	gozle("sifir deger mantigi (var sifir T)", "bildirim", "ilkOge'nin kendi kaynaginda, hep ayni satir")

	bos := sifirDeger[string]()
	gozle("cikarimin yetmedigi cagri", "kullanim", fmt.Sprintf("sifirDeger[string]() aciktan yazildi, %T", bos))
}
```

```
-- bag nerede kuruluyor: genel fonksiyon --
tip parametresi (tamsayi dilimi)           kullanim   cikarildi, T=int
tip parametresi (dizgi dilimi)             kullanim   cikarildi, T=string
tip parametresi (ondalik dilimi)           kullanim   cikarildi, T=float64
sifir deger mantigi (var sifir T)          bildirim   ilkOge'nin kendi kaynaginda, hep ayni satir
cikarimin yetmedigi cagri                  kullanim   sifirDeger[string]() aciktan yazildi, string
```

Her çağrı, genel fonksiyonun bir **örneklemesidir**: `ilkOge`, `T=int` ile örneklendiğinde
bir `int` dilimi üzerinde çalışan bir işleve, `T=string` ile örneklendiğinde bir dizgi
dilimi üzerinde çalışan başka bir işleve karşılık gelir — ikisi de aynı kaynaktan, ama
farklı bir tip bağlanmış olarak. Örnekleme sözcüğü bu dersten sonra iki kez daha
karşımıza çıkacak: Tip Kısıtları dersinde bir kısıtın hangi örneklemeleri reddettiği,
Genel Veri Yapıları dersinde bir örneklemenin kimin bildiriminde durduğu ölçülecek. Bu
derste örnekleme her zaman **çağıranın** tarafında, argümanın statik tipine bakılarak
oluyor — fonksiyonun kendisi hangi örneklemelerin isteneceğini bilmiyor.

`var sifir T` satırının üç ayrı örneklemede üç ayrı sonuç vermesi de aynı mekanizmadan
geliyor. Sıfır Değerler dersi (M09/K01) her tipin kendi sıfır değerini tanımlı bulduğunu
göstermişti — `int` için `0`, `string` için boş dizgi, `float64` için `0.0`. `var sifir T`
bu tanımı genel fonksiyonun içine taşıyor: kaynak hangi sıfır değerin üretileceğini hiç
söylemiyor, yalnız "burada `T`nin sıfır değeri dursun" diyor ve derleyici örnekleme
kurulduğunda bu sözü doldurur. Bu satırın kendisi bildirimde sabit kalırken, doldurduğu
değer her örneklemede kullanım yerinden geliyor — aynı ayrımın küçük bir tekrarı.

**GN2.** `ilkOge` kaynağı üç çağrı boyunca tek harf değişmeden kalıyor; değişen yalnız
argümanın taşıdığı dilimin öğe tipi. `%T` üç ayrı yanıt veriyor — `int`, `string`,
`float64` — ama bu üç yanıtın hiçbiri kaynakta aranarak bulunamaz, çünkü kaynakta `T`nin
yerini alacak bir ad hiç yazılmamış. Derleyici bu bağı, tıpkı Arayüz Kavramı dersinin
örtük gerçekleştirimi gibi, **kaynağın dışında** kuruyor — ama arayüzden farklı olarak bu
kararı çalışma zamanına bırakmıyor, derleme sırasında, argümanın statik tipine bakarak
veriyor. Bu, iki bağın ortak noktası: ikisi de kullanım yerinde kurulur. Farkları da burada
başlıyor: arayüz uyumu değere sorularak doğrulanabilir (`%T` ile), genel fonksiyonun tip
parametresi ise derleme bittiğinde ortadan kalkar ve çalışan programda hiçbir yerde
saklanmaz — bu ayrım Ne Zaman Genellik dersinde tekrar ölçülüyor.

#### Çıkarımın Yetmediği Yer

Derleyici tip parametresini yalnız **argümanlardan** okuyabiliyor; argüman yoksa okuyacak
bir şey de yok. `sifirDeger[T any]() T` bunun en yalın örneği: fonksiyonun hiç parametresi
yok, yalnız bir dönüş tipi var ve dönüş tipinden geriye doğru çıkarım yapılamıyor.
Yukarıdaki koşumda `sifirDeger[string]()` çağrısı bu yüzden `T`yi köşeli parantezle
**açıkça** taşıyor; bu satırdan `[string]`i silmek, "hangi tipin sıfır değeri" sorusunu
derleyicinin yanıtlayamayacağı bir soruya çevirir ve kaynak derlenmez.

Çıkarımın yetmediği ikinci bir durum, argüman **var olduğunda** da ortaya çıkabilir:
aynı tip parametresi birden çok parametrede geçiyorsa, derleyici hepsinin **aynı** somut
tipe çözülmesini bekler. Aşağıdaki ölçüm bunu, kaynağı program içinden derleyiciye
sorarak sınıyor.

```go
// sinirlayici.go — cikarimin yetmedigi cagri ve kisitsiz govdenin sinirlari, derleyiciye sorularak olculur
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

func siniflaKaynak(kaynak string) string {
	if derlenirMi(kaynak) {
		return "derlendi"
	}
	return "derlenmedi"
}

func main() {
	cagriUyumsuz := `package sinama
func Ikisi[T any](a, b T) T { return a }
func F() { Ikisi(3, "x") }`

	cagriAcik := `package sinama
func Ikisi[T any](a, b T) T { return a }
func F() { Ikisi[any](3, "x") }`

	karsilastirma := `package sinama
func Esit[T any](a, b T) bool { return a == b }`

	toplama := `package sinama
func Topla[T any](a, b T) T { return a + b }`

	uzunluk := `package sinama
func Uzunluk[T any](d []T) int { return len(d) }`

	fmt.Printf("%-38s %s\n", "islem", "sonuc")
	fmt.Printf("%-38s %s\n", "cikarim: int ile string ayni T'ye", siniflaKaynak(cagriUyumsuz))
	fmt.Printf("%-38s %s\n", "aciktan T=any yazilinca", siniflaKaynak(cagriAcik))
	fmt.Printf("%-38s %s\n", "govdede kisitsiz T ile == ", siniflaKaynak(karsilastirma))
	fmt.Printf("%-38s %s\n", "govdede kisitsiz T ile +", siniflaKaynak(toplama))
	fmt.Printf("%-38s %s\n", "govdede kisitsiz T'nin dilimine len", siniflaKaynak(uzunluk))
}
```

```
islem                                  sonuc
cikarim: int ile string ayni T'ye      derlenmedi
aciktan T=any yazilinca                derlendi
govdede kisitsiz T ile ==              derlenmedi
govdede kisitsiz T ile +               derlenmedi
govdede kisitsiz T'nin dilimine len    derlendi
```

**GN3.** İlk iki satır aynı çağrının iki yazımı: `Ikisi(3, "x")` derlenmiyor çünkü
derleyici `T`yi birinci argümandan `int`, ikincisinden `string` olarak okumaya çalışıyor ve
tek bir tipte uzlaşamıyor; `Ikisi[any](3, "x")` derleniyor çünkü tip parametresi artık
çıkarıma bırakılmıyor, çağıran `any`i **kendisi** yazıyor. Çıkarım bir kolaylıktır, bir
zorunluluk değil — derleyici her zaman açık yazıma geri dönmeye izin verir, yalnız
çıkarımın **yapılamayacağı** yerde açık yazım zorunlu hâle gelir.

Bu ret, iki parametrenin **aynı** tip parametresini paylaşmasından geliyor —
`func Ikisi[T any](a, b T) T` imzası, `a` ile `b`nin aynı somut tipe çözülmesini bir
bildirim olarak dayatıyor. İmza `func Ikisi2[T1, T2 any](a T1, b T2)` yazsaydı, `a` `int`e,
`b` `string`e ayrı ayrı bağlanır ve çağrı derlenirdi; iki değişkenin farklı tip olmasına izin
verilmesi ya da verilmemesi, tip parametresi listesinin **kaç ayrı ad** taşıdığına bağlıdır.
Bu da bağın konumunu bir kez daha doğruluyor: kaç tip parametresinin paylaşılacağı kaynakta
yazılıdır (bildirim), o parametrelerin hangi somut tiplere karşılık geleceği ise her çağrıda
yeniden kurulur (kullanım yeri).

Bu ayrım pratikte bir tasarım kararına dönüşüyor: iki parametrenin aynı tipte olması
**gerekiyorsa** — örneğin bir karşılaştırma ya da bir takas işlevinde — tek bir tip
parametresi paylaştırmak bu zorunluluğu derleyiciye devrediyor; çağıran yanlışlıkla iki
ayrı tip geçirirse kaynak hiç derlenmiyor. İki parametrenin bağımsız olması **gerekiyorsa**
ayrı tip parametreleri açmak, derleyicinin gereksiz yere bir eşitlik dayatmasını önlüyor.
Kaynaktaki tip parametresi listesi böylece yalnız bir söz dizimi ayrıntısı değil, hangi
argümanların birbirine bağlı olduğunu söyleyen küçük bir sözleşme.

#### Sınırlayıcı Ölçüm: Kısıtsız Gövdede Ne Yazılabilir

**GN4.** Son üç satır farklı bir soruyu ölçüyor: tip parametresi çağrı yerinde kurulduktan sonra,
fonksiyonun **gövdesinde** o parametreyle ne yapılabilir? `any` yalnız "her tip kabul edilir"
demek, "her işlem yazılabilir" demek değil. `a == b` derlenmiyor, çünkü `T`nin kısıtı `any` ve
`any` karşılaştırılabilirliği garanti etmiyor — bir dilim ya da bir işlev de `any`ye uyar, ama
ikisi de `==` ile karşılaştırılamaz. `a + b` da aynı nedenle derlenmiyor: `+` yalnız sayısal
tiplerde ve dizgide tanımlı; `any` bunu garanti etmiyor. `len(d)` ise derleniyor, çünkü bu işlem
`T`nin kendisine değil dilimin tipine bakıyor — `d`nin uzunluğu, öğelerin tipinden bağımsız.

Bu üç satır kısıtsız bir tip parametresinin gövdede neredeyse işe yaramaz olduğunu
gösteriyor: `any` en geniş kabulü sağlıyor ama en dar işlem kümesini bırakıyor. Bu
sınırlama bir eksiklik değil, kursun ikinci iddiasının ters yüzü: bir tipin bir arayüzü
örtük karşılaması kaynakta hiçbir yerde yazılmıyorsa, derleyicinin `T` hakkında **hiçbir
şey bilmediği** de kaynakta hiçbir yerde yazılmıyor demektir; `any` bu bilgisizliği en uç
noktasına taşıyor.

`len(d)` çizgisinin diğer ikisinden ayrı davranmasının nedeni, bu işlemin `T`ye değil
dilimin **kendi yapısına** bakmasıdır: bir dilim, öğe tipi ne olursa olsun, aynı üç alanlı
başlığı taşır ve uzunluk bu başlıktan okunur. `index`, `range`, `cap` gibi diğer yerleşik
işlemler de aynı sınıftan — bunlar `T`nin hangi tip olduğunu hiç sormaz, yalnız dilin
kendisinin garanti ettiği yapıya bakar. `==` ve `+` ise tam tersi: ikisi de `T`nin somut
tipine özgü bir davranışı çağırıyor ve kısıtsız bir `T` bu davranışın **var olduğunu bile**
garanti etmiyor. Sıradaki ders bu boşluğu dolduran mekanizmayı — tip parametresine hangi
işlemlerin izin verileceğini **bildirimde** söyleyen tip kısıtını — kurar; `comparable` adlı
yerleşik kısıt, yukarıdaki `Esit` örneğini tek bir sözcükle derlenir hâle getirir.

Beş satırlık ölçümün tamamı `derlenirMi` adlı tek bir araçla üretildi: kaynağı geçici bir
pakete yazıp derleyiciyi bir alt süreç olarak çağıran ve yalnız çıkış durumunu okuyan aynı
yöntem, Go'nun Tasarım Amaçları dersinde de kullanılmıştı. Yöntem burada da aynı iki
özelliği taşıyor — belirlenimci (aynı kaynak her koşumda aynı yanıtı verir, çünkü derleyicinin
kendisi çağrılıyor) ve taşınabilir (yalnız standart kitaplığın süreç başlatma yeteneğine
dayanıyor). Hata metninin hiç okunmaması bilinçli bir sınırdır: bu ders "neden reddedildi"
sorusunu değil, "reddedildi mi" sorusunu ölçüyor, çünkü derleyicinin ürettiği hata metninin
biçimi sürüme bağlı bir ayrıntıdır ve kursun ölçütü bu ayrıntıya dayanmıyor.

#### Özet

- Genel bir fonksiyonun tip parametresi kaynakta hiçbir yerde yazılı değildir; derleyici
  onu çağrı yerinde, argümanların statik tipine bakarak kurar.
- Fonksiyon gövdesindeki sıfır değer mantığı gibi tipten bağımsız kod bildirimde durur ve
  her çağrıda değişmez; tip parametresinin kendisi her çağrıda kullanım yerinde yeniden
  kurulur.
- Çıkarım yalnız argümanlardan okunur: argüman yoksa (dönüş tipi tek başına) ya da
  argümanlar aynı tipe uzlaşmıyorsa, tip parametresinin çağrı yerinde açıkça yazılması
  gerekir.
- Kısıtsız bir tip parametresi (`any`) gövdede yalnız her tipte geçerli işlemlere izin
  verir; `==` ve `+` gibi işlemler kısıtsız `T` ile derlenmez.
- Bütün bu ölçümler derleyicinin kendisine sorularak yapıldı: hata metni okunmadı, yalnız
  bir kaynağın derlenip derlenmediği sınıflandı.

Bu sınır, genelliğin bir eksikliği değil, tanımının doğal bir sonucu: bir genel fonksiyon
tek bir kaynaktan her `T` için çalışmak zorunda ve bu yalnız `T`nin somut tipinden
bağımsız işlemlerle mümkün. Kısıt bu bağımsızlığı bozmadan hangi işlemlerin güvenle
kullanılabileceğini genişletiyor.

#### Sonraki Adım

Kısıtsız `T` ile `==` ve `+` yazılamaması bir eksiklik gibi görünse de, aslında derleyicinin
`T` hakkında **bilmediği** her şeyi olduğu gibi bırakmasının sonucu. Sıradaki ders bu
bilgisizliği bir bildirimle kapatır: bir tip kısıtı, tip parametresinin kabul ettiği
tiplerin kümesini ve o kümenin hangi işlemleri garanti ettiğini kaynakta **açıkça** yazan
bir arayüzdür — ve bu arayüz, öteki arayüzlerden farklı bir işte kullanılır.

### Tip Kısıtları

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/tip-kisitlari

Bir tip kısıtı da bir arayüzdür, ama yöntem değil tip kümesi listeler: aynı üç kısıt dört adayla sınandığında SadeceInt yalnız int'i, ~int içeren TemelTipiInt de temeli int olan adlandırılmış tipleri, ~int|~float64 birleşimi Sayisal da ikisini birden kabul ediyor — 1/4, 2/4, 3/4. Sınırlayıcı ölçüm, tip kümesi listeleyen bir arayüzün sıradan bir değer tipi olarak kullanılamadığını, yalnız yöntem listeleyen bir arayüzün hem kısıt hem değer tipi olabildiğini gösteriyor.

Genel Fonksiyonlar dersi kısıtsız bir tip parametresinin gövdede neredeyse işe yaramaz
olduğunu ölçtü: `any` ile `==` de `+` de derlenmiyordu, yalnız dilimin kendi yapısına
bakan `len` gibi işlemler geçiyordu. O ders bu boşluğu dolduran mekanizmayı bir cümleyle
açık bırakmıştı: bir tip kısıtı, tip parametresinin kabul ettiği tiplerin kümesini ve o
kümenin garanti ettiği işlemleri kaynakta **açıkça** yazan bir arayüzdür. Bu ders o
cümleyi açıyor ve aynı soruyu tekrar soruyor: bu kısıt kaynakta nerede duruyor ve neyi
kabul ettiğini nereden biliyoruz?

Yanıt kısa: kısıt **bildirimde** durur. Ortak ölçümde "tip kısıtının bildirimi" dört
bildirim örneğinden biridir — gömme, derleme zamanı uyum kanıtı ve genel tipin
örneklenmesiyle birlikte, kaynakta açıkça yazılan dört bağdan biri. Bu ders o bağın
nasıl yazıldığını ve neyi sınırladığını ölçer: bir kısıt hangi tipleri kabul ediyor, bu
kabulü genişletmenin bir bedeli var mı ve kısıt arayüzünün kendisi başka nerelerde
kullanılabiliyor.

#### Kısıt Bir Tip Kümesi Listeler

**GN5.** Sıradan bir arayüz bir **yöntem kümesi** listeler: `interface{ Yaz() string }`
diyen bir tip, `Yaz` adlı bir yöntemi olan her tipi kabul eder. Bir tip kısıtı da
sözdizimsel olarak bir arayüzdür — `interface{ ... }` yazılır — ama parantez içine yöntem
imzası yerine **tip adları** yazılabilir: `interface{ int | float64 }`, yalnız `int` ya da
yalnız `float64` olan tipleri kabul eden bir **tip kümesi** tanımlar. Bu, aynı sözdiziminin
iki ayrı işte kullanılması: biri "bu yöntemleri sağla" diyor, öbürü "bu tiplerden biri ol"
diyor.

`~` işareti bu kümeyi genişletir. `~int`, yalnız `int`in kendisini değil, **temel tipi**
`int` olan her adlandırılmış tipi de kapsar — `type Yas int` yazıldığında `Yas` artık
`~int`in kümesine girer, ama `int`in kümesine girmez, çünkü `Yas` ile `int` aynı tip
değildir, yalnız aynı temel tipi paylaşırlar. Aşağıdaki ölçüm üç kısıtı dört adayla
sınıyor: `int`in kendisi, temeli `int` olan adlandırılmış bir tip (`Yas`), `float64` ve
hiçbirine uymayan `string`.

```go
// kisit.go — uc kisitin kac tipi kabul ettigi, derleyiciye sorularak sayiliyor
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

type kisitTanimi struct{ ad, govde string }
type tipTanimi struct{ ad, bildirim, deger string }

func main() {
	kisitlar := []kisitTanimi{
		{"SadeceInt", "interface{ int }"},
		{"TemelTipiInt", "interface{ ~int }"},
		{"Sayisal", "interface{ ~int | ~float64 }"},
	}
	tipler := []tipTanimi{
		{"int", "", "3"},
		{"Yas (temeli int)", "type Yas int\n", "Yas(3)"},
		{"float64", "", "3.5"},
		{"string", "", `"x"`},
	}

	fmt.Printf("%-16s", "kisit")
	for _, t := range tipler {
		fmt.Printf("%-20s", t.ad)
	}
	fmt.Println()

	for _, k := range kisitlar {
		fmt.Printf("%-16s", k.ad)
		kabul := 0
		for _, t := range tipler {
			kaynak := fmt.Sprintf("package sinama\n%stype K %s\nfunc F[T K](a T) {}\nfunc G() { F(%s) }",
				t.bildirim, k.govde, t.deger)
			ok := derlenirMi(kaynak)
			sonuc := "hayir"
			if ok {
				kabul++
				sonuc = "evet"
			}
			fmt.Printf("%-20s", sonuc)
		}
		fmt.Printf("%d/4\n", kabul)
	}
}
```

```
kisit           int                 Yas (temeli int)    float64             string              
SadeceInt       evet                hayir               hayir               hayir               1/4
TemelTipiInt    evet                evet                hayir               hayir               2/4
Sayisal         evet                evet                evet                hayir               3/4
```

**GN6.** Üç satır, aynı dört adayla sınandığında, kısıtın kaynaktaki yazımına göre üç ayrı
kabul sayısı üretiyor. `SadeceInt` yalnız `int`in kendisini kabul ediyor — `Yas`, temeli
`int` olsa bile ayrı bir tip olduğu için reddediliyor; bu, iki dizinin uzunluğu farklı
olduğunda ayrı tip sayılmasıyla aynı katılıktır (Diziler ve Dilimler dersi, M09/K01).
`TemelTipiInt` `~int` yazdığı için `Yas`ı da kabul ediyor ve kabul sayısı ikiye çıkıyor.
`Sayisal` iki `~` teriminin birleşimini (`|`) listelediği için hem tam sayı hem ondalık
tarafını kapsıyor ve üçe çıkıyor — `string` ise üçünde de reddediliyor, çünkü hiçbir terim
onu kapsamıyor. Kabul sayısındaki bu artış kaynaktan doğrudan okunabilir: `SadeceInt`in
gövdesinde bir terim var, `TemelTipiInt`inkinde bir terim ama tilde'li, `Sayisal`ınkinde
iki terim birleşimli. Kısıtın ne kadar geniş olduğu, kısıtın **kendi bildirimindeki** terim
sayısına ve tilde işaretine bakılarak, hiçbir çağrı yapılmadan önceden söylenebilir.

Bu ölçüm aynı zamanda bağın bozulduğunda nerede göründüğünü de gösteriyor: bir çağıran
kısıtın kümesinde olmayan bir tiple bir genel fonksiyonu örneklemeye çalıştığında, hata
çalışma zamanında değil **derlemede** çıkıyor — tablodaki her "hayir" bir derleme reddi,
`string` argümanıyla `SadeceInt` kısıtlı bir fonksiyonu çağırmaya çalışmanın bir panikle
değil bir derleme durmasıyla sonuçlanacağının kanıtı.

Kısıtın kendisi de örtük bir gerçekleştirim modeliyle çalışıyor: `Yas` tipi hiçbir yerde
"ben `TemelTipiInt`i karşılıyorum" demiyor, `type Yas int` satırı bile bir kısıttan hiç
söz etmiyor. Bir tipin bir kısıtın kümesine girip girmediği, o tipin kendi kaynağına değil
temel tipine bakılarak, dışarıdan belirleniyor — Arayüz Kavramı dersinin ölçtüğü örtük
gerçekleştirim modeliyle aynı düzenek, burada yöntemler yerine tip kimliği üzerinden
işliyor. Fark şurada: bir arayüzü örtük karşılamak için bir tipin belirli yöntemleri
**yazması** gerekiyor, bir kısıtı örtük karşılamak için ise bir tipin hiçbir şey
yazmasına gerek yok — yalnız temel tipinin kısıtın listelediği terimlerden birine uyması
yeterli. `Yas` tipini tanımlayan satır, kısıt tanımlanmadan önce de yazılmış olabilirdi;
kısıt sonradan geldiğinde `Yas` otomatik olarak onun kümesine girdi, çünkü üyelik
`Yas`ın kaynağına değil temel tipine bakıyor.

`|` işaretiyle ayrılan terimlerin oluşturduğu bu yapıya **küme birleşimi** denir — kısıtın
kabul ettiği tip kümesi, her terimin kendi kümesinin birleşimidir. `Sayisal`ın kümesi
`~int`in kümesiyle `~float64`ün kümesinin birleşimi; bir tip bu iki alt kümeden **birine**
girerse yeterli, ikisine birden girmesi gerekmiyor. Küme birleşimi terimleri istenildiği
kadar çoğaltılabilir (`~int | ~int8 | ~int16 | ~int32 | ~int64`), ama her terim kümeyi
yalnız **genişletir**, hiçbir terim var olan bir üyeliği geri almaz — bu yüzden yukarıdaki
tablo tek yönlü bir artış gösteriyor, 1'den 2'ye, 2'den 3'e; hiçbir kısıt bir öncekinden
daha az tip kabul etmiyor.

Kısıt genişledikçe verdiği garanti daralıyor: `SadeceInt` yalnız bir tipi kabul ediyor ama
o tipin bütün `int` işlemlerini (bit kaydırma dâhil) garanti ediyor; `Sayisal` üç tipi daha
kabul ediyor ama yalnız her iki ailenin de ortak desteklediği işlemleri (`+`, `-`, `*`, `/`,
karşılaştırma işleçleri) garanti ediyor — bit kaydırma `float64` için tanımlı olmadığından
`Sayisal` kısıtlı bir gövdede yazılamaz. Kısıtın kaynaktaki genişliği ile gövdenin
yazabileceği işlemlerin darlığı ters orantılı; bu, Genel Fonksiyonlar dersinin `any`
kısıtıyla gösterdiği en uç durumun (hiçbir işlem yazılamaması) daha ölçülü bir hâli.

#### `comparable`: Kısıt Boşluğunu Kapatan Yerleşik Kısıt

Genel Fonksiyonlar dersinin son ölçümü, kısıtsız `T` ile `a == b` yazan bir kaynağın
derlenmediğini göstermişti. Nedeni artık kaynaktan okunabilir: `any` hiçbir tip kümesi
terimi taşımıyor, yalnızca "her tip kabul edilir" diyor ve `==` işlecinin var olduğuna
dair hiçbir söz vermiyor. Dil bu boşluğu kendi yerleşik kısıtıyla kapatıyor:
`comparable`, `==` ve `!=` işleçlerini destekleyen her tipi kapsayan bir tip kümesi
tanımlar — dilim, sözlük ve fonksiyon tipleri hariç, çünkü bu üçü Go'da hiçbir zaman
`==` ile karşılaştırılamaz.

```go
// kapali.go — comparable kisiti gecen dersin kisitsiz == hatasini kapatiyor
package main

import "fmt"

func Esit[T comparable](a, b T) bool { return a == b }

func main() {
	fmt.Println("Esit(3, 3)     :", Esit(3, 3))
	fmt.Println("Esit(\"x\", \"y\") :", Esit("x", "y"))
}
```

```
Esit(3, 3)     : true
Esit("x", "y") : false
```

**GN7.** `Esit` burada, önceki dersin `Esit[T any]` yazımıyla aynı gövdeye sahip — tek fark
kısıtın adı. Bu tek sözcük, `==` işlecinin gövdede kullanılabilmesi için gereken bilgiyi
taşıyor; `comparable` `any` gibi standart kitaplığın bir parçası değil, dilin kendi
sözdiziminin bir üyesidir, tıpkı `any`nin kendisi gibi. Bağın nerede kurulduğu burada
iki katmanlı: kısıtın **hangi işlemlere izin verdiği** kısıtın kendi bildiriminde durur
(`comparable` mı `any` mi seçildi), ama o izin verilen işlemin **hangi somut tip için**
çalışacağı yine çağrı yerinde, örnekleme sırasında kurulur.

#### Sınırlayıcı Ölçüm: Kısıt Arayüzü Yalnız Kısıt Konumunda Geçerli

Yukarıdaki üç kısıt da birer arayüz. Sıradan bir arayüz iki işte kullanılabilir: bir
değişkenin tipi olarak (`var x Yazici`) ve bir tip parametresinin kısıtı olarak
(`func F[T Yazici](...)`). Tip kümesi listeleyen bir kısıtın bu iki işten yalnız birini
yapabildiği, aşağıdaki ölçümün konusu.

```go
// sinirlayici.go — kisit arayuzu deger tipi olarak kullanilamiyor, derleyiciye sorularak olculuyor
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

func siniflaKaynak(kaynak string) string {
	if derlenirMi(kaynak) {
		return "derlendi"
	}
	return "derlenmedi"
}

func main() {
	sayisalKisit := `package sinama
type Sayisal interface{ ~int | ~float64 }
func Topla[T Sayisal](a, b T) T { return a + b }`

	sayisalDeger := `package sinama
type Sayisal interface{ ~int | ~float64 }
func F() { var x Sayisal; _ = x }`

	yaziciKisit := `package sinama
type Yazici interface{ Yaz() string }
func F[T Yazici](a T) {}`

	yaziciDeger := `package sinama
type Yazici interface{ Yaz() string }
func F() { var x Yazici; _ = x }`

	fmt.Printf("%-38s %-12s %s\n", "arayuz", "kisit konumu", "deger tipi")
	fmt.Printf("%-38s %-12s %s\n", "Sayisal (tip kumesi listeler)",
		siniflaKaynak(sayisalKisit), siniflaKaynak(sayisalDeger))
	fmt.Printf("%-38s %-12s %s\n", "Yazici (yontem listeler)",
		siniflaKaynak(yaziciKisit), siniflaKaynak(yaziciDeger))
}
```

```
arayuz                                 kisit konumu deger tipi
Sayisal (tip kumesi listeler)          derlendi     derlenmedi
Yazici (yontem listeler)               derlendi     derlendi
```

**GN8.** `Sayisal`, bir kısıt konumunda (`[T Sayisal]`) derleniyor ama sıradan bir
değişken tipi olarak (`var x Sayisal`) derlenmiyor: tip kümesi terimi taşıyan bir arayüz,
kaynağın söylediği kadarıyla, yalnız kısıt olarak var olabilir. `Yazici` ise ikisinde de
derleniyor — yöntem listeleyen bir arayüzün tip kümesi kısıtlaması yok, bu yüzden hem bir
değeri taşıyabilir hem bir kısıt olabilir. Arayüzün iki işi burada ayrışıyor: **sözleşme**
(hangi yöntemler var) ve **kısıt** (hangi tipler geçerli) aynı sözdizimle yazılır, ama
tip kümesi terimi eklendiği an arayüz artık yalnız ikinci işi yapabiliyor.

Bu ayrım Arayüz Kavramı dersinin kurduğu modeli bozmuyor, ona bir sınır ekliyor: bir
arayüzün örtük gerçekleştirimi hâlâ geçerli, ama bir arayüzün **kendisinin** nerede
kullanılabileceği artık arayüzün gövdesindeki terimlere bağlı. `Sayisal` bir tipin ne
yapabileceğini söylemiyor, hangi tipler **olabileceğini** söylüyor — ve bu, bir yöntem
sözleşmesinden farklı bir sorudur. `Topla[T Sayisal]` fonksiyonu `T`nin `+` işlecini
desteklediğini biliyor, çünkü kısıtın tip kümesindeki her terim (`~int`, `~float64`) bu
işleci destekliyor; ama bu bilgi bir yöntem çağrısından değil, kümenin **üyeliğinden**
geliyor.

Bu derste iki yer yan yana kondu ve fark her ikisinde de koşturularak gösterildi: kısıtın
kendisi bildirimde durur (kaç terim, hangi tilde, hangi birleşim), ama kısıtın hangi somut
tipe uygulanacağı yine kullanım yerinde, örnekleme sırasında kurulur. Genel Fonksiyonlar
dersi bu ikinci yeri tek başına ölçmüştü; bu ders ona bildirim tarafını ekledi ve ikisinin
birlikte nasıl çalıştığını gösterdi — kısıt olmadan kullanım yeri hiçbir işleme izin
vermiyordu, kısıtla birlikte hangi işlemlerin izinli olduğu önceden, kaynaktan okunabilir
hâle geliyor.

Bu iki işin aynı sözdizimle yazılması, kısıt kavramının Go'ya sonradan eklenmiş ayrı bir
yapı olmamasının doğrudan sonucu: dil yeni bir anahtar sözcük açmak yerine mevcut arayüz
sözdizimini genişletti ve tip terimlerini de kabul eder hâle getirdi. Bunun bedeli
yukarıdaki ölçümde görüldü — genişleyen sözdizimin bir ucu (tip terimi eklemek) öbür ucu
(değer tipi olarak kullanmak) kapatıyor. Bir kısıt yazan kişi bu yüzden iki soruyu
birlikte düşünmek zorunda: kısıtın kabul edeceği tipler ne kadar geniş olmalı ve bu
kısıtın bir gün sıradan bir değer tipi olarak da kullanılması gerekip gerekmeyeceği. İkinci
soru evetse, kısıt tip terimi taşımamalı — yalnız yöntem listelemeli; kısıt yalnız tip
parametrelerini sınırlamak için varsa, tip terimi eklemenin bir bedeli yok, çünkü zaten
başka bir işte kullanılması beklenmiyor.

#### Özet

- Bir tip kısıtı sözdizimsel olarak bir arayüzdür, ama yöntem yerine bir **tip kümesi**
  listeler; hangi tiplerin kabul edildiği kısıtın kendi bildiriminden okunur.
- `~T` yalnız `T`nin kendisini değil, temel tipi `T` olan her adlandırılmış tipi de
  kapsar; `|` ile birleştirilen terimler kümeyi genişletir.
- Aynı dört aday üç kısıtla sınandığında kabul sayısı kısıttaki terim sayısına ve tilde
  işaretine göre artıyor: 1/4, 2/4, 3/4.
- Kısıt bozulduğunda — bir çağıran kümede olmayan bir tiple örnekleme yaptığında — bağ
  çalışma zamanında değil derlemede görünür.
- Tip kümesi terimi taşıyan bir arayüz yalnız kısıt konumunda geçerlidir; yöntem
  listeleyen bir arayüz hem kısıt hem sıradan değer tipi olabilir.
- Arayüzün iki işi — sözleşme ve kısıt — aynı sözdizimle yazılır, ama tip kümesi terimi
  eklendiği an arayüz yalnız ikinci işe daralır.

Kısıt, kısıtsız `any`nin çizdiği en dar sınırı genişletiyor, ama sınırı tamamen
kaldırmıyor: bir kısıt ne kadar geniş yazılırsa yazılsın, gövde yine yalnız kısıtın
garanti ettiği işlemleri kullanabilir. Bu, tip güvenliğinin bedelinin nereye gittiğini
gösteriyor — güvenlik, gövdenin yazabileceklerini kısıtın bildiriminde önceden
sınırlamaktan geliyor.

#### Sonraki Adım

Kısıt, bir tip parametresinin kabul edildiği tipleri bildirimde sınırlıyor; ama kısıtın
kendisi, o tip parametresiyle kurulan bir **veri yapısının** nerede örnekleneceğini
söylemiyor. Sıradaki ders bunu ölçer: bir genel yapı `Yigin[int]` ve `Yigin[string]`
olarak iki kez örneklendiğinde, bu örneklemenin hangi tarafın bildiriminde — yapıyı yazan
tarafın mı, kullanan tarafın mı — durduğunu ve bu durmanın tip güvenliğine ne kazandırdığını.

### Genel Veri Yapıları

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/genel-veri-yapilari

Bir genel veri yapısının tip parametresi çağıranın bildiriminde açıkça yazılır: Yigin[int] ile Yigin[string] %T'de iki ayrı dinamik tip olarak görünüyor ve biri diğerinin yerine geçemiyor. Tip güvenliğinin ölçülebilir karşılığı: boş arayüzle yazılan aynı yapıda elemanı kullanmak için bir tip iddiası gerekirken genel yapıda sıfır. Sınırlayıcı ölçüm, karşılaştırılabilirliğin hem kapsayıcının kendi alan yapısına hem tip parametresinin kendisine bağlı olduğunu gösteriyor.

Tip Kısıtları dersi bir bağın iki katmanda kurulduğunu gösterdi: kısıtın izin verdiği
işlemler bildirimde durur, o işlemlerin hangi somut tip için çalışacağı kullanım yerinde
kurulur. Bu ders aynı iki katmanı bir veri yapısı üzerinde ölçer, ama bir farkla: bir genel
fonksiyonun tip parametresi çağrı yerinde **çıkarılıyordu**, kaynakta hiç yazılmıyordu.
Bir genel veri yapısının tip parametresi ise çağıranın kendi bildiriminde **açıkça
yazılır** — `Yigin[int]` diyen taraf, `[int]`i kendi eliyle koyar, derleyici bunu bir
argümandan okumaz. Aynı soru, iki farklı yanıt: bağ yine kaynağın dışında (fonksiyonun
kendi tanımının dışında) kuruluyor, ama bu kez çıkarımla değil, çağıranın yazdığı bir
bildirimle.

Bu farkın kaynağı, bir fonksiyon çağrısıyla bir tip bildirimi arasındaki farkla aynı yerde
duruyor. Bir fonksiyon çağrısında argümanlar zaten var — derleyici onların tipine bakıp
`T`yi çıkarabiliyor. Bir tip bildiriminde ise henüz hiçbir argüman yok; `Yigin[int]` bir
değer değil, bir **tip adı** ve bir tip adının hangi tip parametresiyle örnekleneceğini
çıkaracak bir argüman ortada bulunmuyor. Derleyicinin çıkarım yapabilmesi için okuyacağı
bir şeye ihtiyacı var; genel fonksiyonda bu, çağrının argümanlarıdır, genel tipte ise
böyle bir argüman yok — çağıranın kendisi yazmak zorunda.

#### Örnekleme Çağıranın Bildiriminde Duruyor

Bir genel veri yapısı, adının hemen ardına bir tip parametresi listesi ekleyen bir tip
bildirimidir: `type Yigin[T any] struct{ ogeler []T }`. Bu tek kaynak, `Yigin[int]`
için de `Yigin[string]` için de kullanılabilir bir **tip güvenli kapsayıcı** kalıbı
kurar — ama kullanılabilmesi için önce **örneklenmesi** gerekir ve bu örnekleme her
zaman kullanan tarafın kendi bildiriminde yazılıdır: bir değişken tanımında, bir alan
tipinde ya da bir dönüş tipinde.

`Yigin`in yöntemleri kendi tip parametresini yeniden tanımlamıyor; alıcı yazımındaki
`(y *Yigin[T])` ifadesi, tipin kendi bildiriminde açılmış olan `T`yi yönteme **taşıyor**.
`Ekle(o T)` ve `Tepe() (T, bool)` bu yüzden yeni bir köşeli parantez açmıyor — kullandıkları
`T`, `Yigin`in kendisiyle birlikte, `Yigin[int]` örneklendiği anda zaten `int`e
bağlanmış oluyor. Örnekleme bir kez, tipin kendi bildiriminde oluyor; o tipe bağlı her
yöntem bu tek örneklemeyi paylaşıyor.

```go
// yigin.go — genel veri yapisinin ornek(leme)si cagiranin bildiriminde
package main

import "fmt"

type Yigin[T any] struct{ ogeler []T }

func (y *Yigin[T]) Ekle(o T) { y.ogeler = append(y.ogeler, o) }

func (y *Yigin[T]) Tepe() (T, bool) {
	var sifir T
	if len(y.ogeler) == 0 {
		return sifir, false
	}
	return y.ogeler[len(y.ogeler)-1], true
}

func main() {
	var tamsayiYigini Yigin[int]
	tamsayiYigini.Ekle(3)
	tamsayiYigini.Ekle(5)

	var dizgiYigini Yigin[string]
	dizgiYigini.Ekle("elma")

	t, _ := tamsayiYigini.Tepe()
	d, _ := dizgiYigini.Tepe()

	fmt.Printf("%-24s tip=%-16T deger=%v\n", "tamsayiYigini", tamsayiYigini, t)
	fmt.Printf("%-24s tip=%-16T deger=%v\n", "dizgiYigini", dizgiYigini, d)
	fmt.Println("sonuc uzerinde aritmetik:", t+1)
}
```

```
tamsayiYigini            tip=main.Yigin[int]  deger=5
dizgiYigini              tip=main.Yigin[string] deger=elma
sonuc uzerinde aritmetik: 6
```

**GN9.** `var tamsayiYigini Yigin[int]` satırının kendisi, `T`nin `int` olacağını söyleyen bir
**bildirimdir** — Genel Fonksiyonlar dersindeki `ilkOge([]int{7, 2, 9})` çağrısıyla
karşılaştırıldığında fark açık: orada `T`nin ne olduğu argümanın tipinden **çıkarılıyordu** ve
kaynakta hiçbir yerde `int` yazmıyordu; burada `int` satırın içinde, gözle görülür biçimde
duruyor. `%T` bunu doğruluyor: `tamsayiYigini` ile `dizgiYigini` aynı kaynaktan üretildi, ama
çalışma zamanındaki dinamik tipleri `main.Yigin[int]` ve `main.Yigin[string]` — birbirinden
tamamen ayrı iki tip, tıpkı `[4]int` ile `[3]int`in ayrı tip olması gibi (Diziler ve Dilimler,
M09/K01). Ortak ölçümde bu bağ "genel tipin örneklenmesi" adıyla bildirim örneklerinden biri
sayılıyor.

`var tamsayiYigini Yigin[int]` satırının bir çağrı içermediğine de dikkat etmek gerekir — bu bir
sıfır değer bildirimi; `New` benzeri bir yapıcı çağrılmıyor. Sıfır Değerler dersinin (M09/K01)
ölçtüğü gibi, bir yapının sıfır değeri kendi alanlarının sıfır değerlerinden kuruluyor: `ogeler`
alanı burada `nil` bir dilim olarak başlıyor. Bu, örnekleme ile ilk değerin verilmesi arasındaki
farkı gösteriyor: `Yigin[int]` yazmak `T`yi bağlıyor, ama `ogeler`i doldurmuyor; ilk `Ekle`
çağrısı `append`i `nil` bir dilim üzerinde çalıştırıyor ve Değişken Sayıda Argüman dersinin
(M09/K01) ölçtüğü gibi bu sorunsuz işliyor — `nil` dilime eklemek onu büyütüyor, panikletmiyor.

#### Tip Güvenliğinin Ölçülebilir Karşılığı

İki örneklemenin ayrı tip olması bir sonuç doğuruyor: biri diğerinin yerine
**geçemiyor**. Aşağıdaki ölçüm bunu ve aynı işi boş arayüzle yazmanın bedelini,
derleyiciye sorarak sayıyor.

```go
// guvenlik.go — tip guvenliginin olculebilir karsiligi: kac tip iddiasi gerekiyor
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

func siniflaKaynak(kaynak string) string {
	if derlenirMi(kaynak) {
		return "derlendi"
	}
	return "derlenmedi"
}

func main() {
	yerDegistirme := `package sinama
type Yigin[T any] struct{ ogeler []T }
func F() {
	var a Yigin[int]
	var b Yigin[string]
	b = a
	_ = b
}`

	anySizIddia := `package sinama
type YiginAny struct{ ogeler []any }
func (y *YiginAny) Ekle(o any) { y.ogeler = append(y.ogeler, o) }
func (y *YiginAny) Tepe() any { return y.ogeler[len(y.ogeler)-1] }
func F() {
	var y YiginAny
	y.Ekle(3)
	t := y.Tepe()
	_ = t + 1
}`

	anyIleIddia := `package sinama
type YiginAny struct{ ogeler []any }
func (y *YiginAny) Ekle(o any) { y.ogeler = append(y.ogeler, o) }
func (y *YiginAny) Tepe() any { return y.ogeler[len(y.ogeler)-1] }
func F() {
	var y YiginAny
	y.Ekle(3)
	t, tamam := y.Tepe().(int)
	_ = tamam
	_ = t + 1
}`

	genelIleIddiasiz := `package sinama
type Yigin[T any] struct{ ogeler []T }
func (y *Yigin[T]) Ekle(o T) { y.ogeler = append(y.ogeler, o) }
func (y *Yigin[T]) Tepe() T { return y.ogeler[len(y.ogeler)-1] }
func F() {
	var y Yigin[int]
	y.Ekle(3)
	t := y.Tepe()
	_ = t + 1
}`

	fmt.Printf("%-42s %s\n", "durum", "sonuc")
	fmt.Printf("%-42s %s\n", "Yigin[string] = Yigin[int] (yer degistirme)", siniflaKaynak(yerDegistirme))
	fmt.Printf("%-42s %s\n", "any yigininda 0 tip iddiasiyla aritmetik", siniflaKaynak(anySizIddia))
	fmt.Printf("%-42s %s\n", "any yigininda 1 tip iddiasiyla aritmetik", siniflaKaynak(anyIleIddia))
	fmt.Printf("%-42s %s\n", "genel yiginda 0 tip iddiasiyla aritmetik", siniflaKaynak(genelIleIddiasiz))
}
```

```
durum                                      sonuc
Yigin[string] = Yigin[int] (yer degistirme) derlenmedi
any yigininda 0 tip iddiasiyla aritmetik   derlenmedi
any yigininda 1 tip iddiasiyla aritmetik   derlendi
genel yiginda 0 tip iddiasiyla aritmetik   derlendi
```

**GN10.** İlk satır, iki örneklemenin birbirinin yerine geçememesini doğrudan gösteriyor:
`b = a` derlenmiyor, çünkü `Yigin[string]` ile `Yigin[int]` derleyici için `[4]int` ile
`[3]int` kadar ayrı iki tip. Bu, örneklemenin çağıranın bildiriminde durmasının doğrudan
sonucu — bildirimde yazılan tip, o değişkenin kalıcı kimliğinin bir parçası oluyor.

**GN11.** Son üç satır aynı işi iki yazımla karşılaştırıyor. Boş arayüzle yazılan
`YiginAny`, `Tepe()`ten `any` döndürüyor; bu değeri doğrudan `+1` ile toplamaya çalışmak
derlenmiyor, çünkü `any`nin somut tipi kaynakta bilinmiyor. Tek çıkış yolu bir **tip
iddiası** yazmak (`y.Tepe().(int)`) — Tip İddiası ve Tip Anahtarı dersinin ölçtüğü aynı
mekanizma, burada bir kapsayıcının her okuma noktasında tekrar tekrar yazılması gereken bir
yük olarak beliriyor. Genel `Yigin[int]` ise `Tepe()`ten doğrudan `int` döndürüyor ve
`+1` hiçbir tip iddiası yazılmadan derleniyor — tip güvenliğinin ölçülebilir karşılığı bu:
aynı işi yapmak için gereken tip iddiası sayısı `any` yazımında en az bir, genel yazımda
sıfır.

Bu fark kapsayıcı büyüdükçe katlanıyor. `YiginAny`nin her okuma noktası — `Tepe`, bir
`Alt` yöntemi eklense onun dönüşü, bir `Tumu` yöntemi eklense onun döndürdüğü dilimin her
elemanı — kendi tip iddiasını taşımak zorunda, çünkü kapsayıcı tip bilgisini `any`nin
arkasında saklıyor ve her okuyan taraf bu bilgiyi kendi başına geri kazanmalı. `Yigin[T]`de
ise tip bilgisi kapsayıcının kendi tipinin bir parçası; kaç yeni yöntem eklenirse eklensin,
hiçbiri bir tip iddiası yazmak zorunda kalmıyor, çünkü dönüş tipleri zaten `T` — derleme
zamanında bilinen, somut bir tip. Bir tip iddiasının kendisi de bedelsiz değil: yanlış
tipte bir değer eklenmişse (bir `any` kapsayıcıya bu, hiçbir derleme hatası vermeden
mümkün), iddia çalışma zamanında panikler ya da iki yanıtlı biçimde sessizce başarısız
olur — genel yazımda bu hata sınıfının kendisi hiç var olmuyor, çünkü yanlış tipte bir
eleman eklemek zaten derlemede reddediliyor.

#### Sınırlayıcı Ölçüm: Karşılaştırılabilirlik Hem Yapıya Hem T'ye Bağlı

Tip güvenliği bedelsiz değil. Bir genel tipin `==` ile karşılaştırılıp
karşılaştırılamayacağı, yalnız tip parametresine değil, kapsayıcının **kendi alan
yapısına** da bağlı — ve bu iki etken bağımsız çalışıyor.

```go
// karsilastirma.go — genel tipin karsilastirilabilirligi hem kendi alan yapisina hem T'ye bagli
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func derlenirMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

func siniflaKaynak(kaynak string) string {
	if derlenirMi(kaynak) {
		return "derlendi"
	}
	return "derlenmedi"
}

func main() {
	yiginDilimli := `package sinama
type Yigin[T any] struct{ ogeler []T }
func F() bool { var a, b Yigin[int]; return a == b }`

	kutuKarsilastirilabilirT := `package sinama
type Kutu[T any] struct{ Deger T }
func F() bool { var a, b Kutu[int]; return a == b }`

	kutuKarsilastirilamazT := `package sinama
type Kutu[T any] struct{ Deger T }
func F() bool { var a, b Kutu[[]string]; return a == b }`

	fmt.Printf("%-46s %s\n", "durum", "sonuc")
	fmt.Printf("%-46s %s\n", "Yigin[int] == Yigin[int] (dilim alanli)", siniflaKaynak(yiginDilimli))
	fmt.Printf("%-46s %s\n", "Kutu[int] == Kutu[int] (T dogrudan, T=int)", siniflaKaynak(kutuKarsilastirilabilirT))
	fmt.Printf("%-46s %s\n", "Kutu[[]string] == Kutu[[]string] (T=dilim)", siniflaKaynak(kutuKarsilastirilamazT))
}
```

```
durum                                          sonuc
Yigin[int] == Yigin[int] (dilim alanli)        derlenmedi
Kutu[int] == Kutu[int] (T dogrudan, T=int)     derlendi
Kutu[[]string] == Kutu[[]string] (T=dilim)     derlenmedi
```

**GN12.** Üç satır iki bağımsız etkeni ayırıyor. `Yigin[T]` hiçbir zaman karşılaştırılamaz
— `T` `int` olsa bile, çünkü `ogeler` bir **dilim** alanı ve Sözlükler ile Diziler ve
Dilimler derslerinin (M09/K01) ölçtüğü gibi dilimler `==` ile karşılaştırılamaz; bu
kısıtlama kapsayıcının kendi alan yapısından geliyor, tip parametresinden değil. `Kutu[T]`
farklı bir yapı kurar — `Deger` alanı `T`yi doğrudan tutuyor, dilim değil — ve bu durumda
karşılaştırılabilirlik tamamen `T`ye devrediliyor: `Kutu[int]` karşılaştırılabiliyor çünkü
`int` karşılaştırılabilir, `Kutu[[]string]` karşılaştırılamıyor çünkü bu kez `T`nin
kendisi (`[]string`) karşılaştırılamaz bir tip. Genel bir tipin karşılaştırılabilirliği bu
yüzden tek bir soruya indirgenemez; hem kapsayıcının kendi tasarımına hem doldurulan `T`ye
bakmak gerekiyor.

Bu, kısıtların neden yeterli olmadığını da gösteriyor: `Yigin[T any]` kısıtını
`Yigin[T comparable]` yapmak `Kutu` için işe yarasa da `Yigin` için hiçbir şeyi değiştirmez
— `T` ne kadar karşılaştırılabilir olursa olsun, `ogeler` bir dilim olduğu sürece `Yigin`
karşılaştırılamaz kalır. Tip kısıtı yalnız `T`yi sınırlar, `T`yi taşıyan yapının kendi alan
düzenini değil.

Bu ayrımın somut bir karşılığı var: Sözlükler dersi (M09/K01) bir sözlüğün anahtar tipinin
karşılaştırılabilir olmak zorunda olduğunu ölçmüştü. `Yigin[int]` hiçbir zaman bir sözlük
anahtarı olamaz — `T` ne olursa olsun, çünkü karşılaştırılamazlık `ogeler` alanından
geliyor ve bu alan her örneklemede aynen duruyor. `Kutu[int]` ise anahtar olabilir, çünkü
`Deger` alanı `int`i doğrudan tutuyor ve `int` karşılaştırılabilir; `Kutu[[]string]` yine
olamaz, çünkü bu kez taşınan `T`nin kendisi karşılaştırılamaz. Aynı iki kapsayıcı kalıbı,
"bu tip bir sözlük anahtarı olabilir mi" sorusuna kaynaktaki alan yapısına bakılarak
önceden yanıt veriyor — çalışma zamanında bir anahtar denemesi yapmaya gerek kalmadan.

Üç ders boyunca genellik hep kaynağın içinde kaldı: tip parametresi ya çıkarıldı ya
açıkça yazıldı, ama her iki durumda da kaynağı okuyan biri, doğru yere baktığında, hangi
somut tipin kullanıldığını görebildi. Bu, Arayüz Kavramı dersinin ölçtüğü örtük
gerçekleştirimin tam tersi bir tavır: orada bir tipin bir arayüzü karşıladığı hiçbir
yerde yazmıyordu, burada bir kapsayıcının hangi tiple çalıştığı ya çağrının argümanından
ya da bildirimin köşeli parantezinden **her zaman** okunabiliyor. `Yigin[int]`in dinamik
tipi `%T`ye sorulmadan da bilinebiliyor, çünkü kaynağın kendisinde yazılı; bir arayüz
değerinin dinamik tipi ise yalnız değere sorularak öğrenilebiliyordu. Kursun kapanış
dersi bu iki tavrı — hep okunabilir olan ile yalnız değere sorularak öğrenilebilen — aynı
işi yaparak karşılaştırır.

Bu okunabilirlik bedelsiz değil: `Yigin[int]`i okuyan biri `T`nin `int` olduğunu satırın
kendisinden öğreniyor, ama bu bilgi başka bir yerde — Genel Fonksiyonlar dersindeki gibi
bir argümandan — çıkarılamıyor; yazan tarafın onu yazmayı unutması mümkün değil, çünkü
sözdizimi bunu zorunlu kılıyor. Genelliğin kazandırdığı öngörülebilirlik, çağıranın
üstlendiği bu küçük bir yazım yüküyle geliyor.

#### Özet

- Bir genel veri yapısının tip parametresi, genel fonksiyonun aksine, çağıranın kendi
  bildiriminde açıkça yazılır — çıkarılmaz.
- `Yigin[int]` ile `Yigin[string]`, aynı kaynaktan üretilmiş olsalar bile, çalışma
  zamanında `%T` ile okunan iki ayrı dinamik tiptir ve biri diğerinin yerine geçemez.
- Tip güvenliğinin ölçülebilir karşılığı: aynı işi boş arayüzle yazmak en az bir tip
  iddiası gerektiriyor, genel yazımda bu sayı sıfır.
- Bir genel tipin karşılaştırılabilirliği iki bağımsız etkene bağlıdır: kapsayıcının kendi
  alan yapısı (dilim alanı taşıyorsa hiçbir zaman karşılaştırılamaz) ve — kapsayıcı `T`yi
  doğrudan tutuyorsa — `T`nin kendi karşılaştırılabilirliği.
- Bir tip kısıtı yalnız tip parametresini sınırlar; kapsayıcının kendi alan düzeninden
  gelen sınırları değiştiremez.

#### Sonraki Adım

Üç ders boyunca genellik hep tek bir tarafta ölçüldü: bağ ya bildirimde ya kullanım
yerinde kuruldu, hiçbir zaman değere girmedi. Arayüz konusu ise tam tersini ölçmüştü —
tip bilgisi değerin içinde taşınıyordu ve ancak değere sorularak okunabiliyordu. Kursun
son dersi bu iki yazımı ve aralarında duran arayüz sözleşmesini, aynı işi üç kez yaparak
yan yana koyar: hangi yazım kaç tip iddiası gerektiriyor, hangisi derleme zamanında
durup hangisi çalışma zamanına kalıyor ve genellik her zaman kazanıyor mu.

### Ne Zaman Genellik

Source: https://academia.sh/tr/kurslar/go-tip-sistemi/ne-zaman-genellik

Aynı toplam alan hesabı üç yazımla kuruluyor: boş arayüzle üç tip iddiası, genellikle sıfır ama T tek bir somut tipe sabitlenmiş, arayüz sözleşmesiyle yine sıfır ama farklı somut tipler aynı dilimde durabiliyor. Sınırlayıcı ölçüm, T bir arayüze sabitlendiğinde (heterojen bir koleksiyonu kabul etmek için) genelliğin kazancının kaybolduğunu gösteriyor: eleman boyutu 8 bayttan 16 bayta çıkıyor, tıpkı sıradan bir arayüz değeri gibi. Kurs on altı bağın 4'ünü bildirimde, 4'ünü kullanım yerinde, 8'ini değerin içinde buldu.

Genel Veri Yapıları dersi genelliğin üç ders boyunca hep kaynağın içinde kaldığını
gösterdi: tip parametresi ya çıkarıldı ya bildirimde açıkça yazıldı, ama hiçbir zaman
değere girmedi. Arayüz konusu tam tersini ölçmüştü — dinamik tip değerin içinde taşınıyor
ve ancak değere sorularak okunabiliyordu. Bu kapanış dersi iki yazımı ve aralarında duran
arayüz sözleşmesini, **aynı işi üç kez yaparak** yan yana koyar: bir toplam alan hesabı
boş arayüzle, genellikle ve arayüz sözleşmesiyle kurulur ve üçünde de bağın nerede
kurulduğu sayılır.

Kursun üçüncü iddiası buydu: genellik ile arayüz aynı işi iki ayrı yerde yapıyor.
Genellikte bağ bildirimde ve kullanım yerinde kurulur, değere hiç girmez; arayüzde bağ
değere girer. Bu ders bu farkı ilk kez doğrudan koşturarak, aynı hesaplamayı üç kez
yazarak, ölçer.

Önceki üç ders genelliği hep kendi başına ölçtü — arayüzle yan yana koymadan. Bu, kursun
genel çerçevesine bir borç bıraktı: "genellikte bağ değere girmiyor" iddiası, bir
karşılaştırma yapılmadan yalnız bir gözlemdi. Bu ders o borcu kapatıyor: aynı iş hem
genellikle hem arayüzle yazılıp aynı koşumda ölçülüyor ve fark bir iddia olmaktan çıkıp
bir sayıya (8 bayta karşı 16 bayt) dönüşüyor.

#### Aynı İş, Üç Yazım

Ölçülen iş basit: bir koleksiyondaki şekillerin toplam alanını hesaplamak. `Kare` ve
`Daire` kendi `Alan()` yöntemini taşıyor; `Sekil` arayüzü bu tek yöntemi listeliyor. Bu iş
bilinçli seçildi: alan hesabı **tipe göre gerçekten değişiyor** — bir karenin alanı
kenarın karesi, bir dairenin alanı yarıçapın karesiyle bir sabitin çarpımı — ama toplama
işleminin kendisi her tip için aynı. Üç yazımın da başarması gereken iş bu yüzden iki
parçaya ayrılıyor: tipe özgü kısım (`Alan()`in kendi gövdesi) ve tipten bağımsız kısım
(toplamı biriktirmek).

```go
// uc.go — ayni is uc yazimla: bos arayuz, genellik, arayuz sozlesmesi
package main

import (
	"fmt"
	"unsafe"
)

type Kare struct{ Kenar float64 }

func (k Kare) Alan() float64 { return k.Kenar * k.Kenar }

type Daire struct{ YariCap float64 }

func (d Daire) Alan() float64 { return 3.14159 * d.YariCap * d.YariCap }

type Sekil interface{ Alan() float64 }

// 1) bos arayuzle: her eleman icin bir tip iddiasi (tip anahtari dali) gerekiyor.
func toplamAlanAny(d []any) (toplam float64, iddiaSayisi int) {
	for _, e := range d {
		switch v := e.(type) {
		case Kare:
			iddiaSayisi++
			toplam += v.Alan()
		case Daire:
			iddiaSayisi++
			toplam += v.Alan()
		}
	}
	return
}

// 2) genellikle: govde her T icin ayni, T homojen kaldigi surece hic tip iddiasi yok.
func toplamAlanGenel[T Sekil](d []T) (toplam float64) {
	for _, e := range d {
		toplam += e.Alan()
	}
	return
}

// 3) arayuz sozlesmesiyle: farkli somut tipler ayni dilimde, hicbir tip iddiasi yok.
func toplamAlanArayuz(d []Sekil) (toplam float64) {
	for _, e := range d {
		toplam += e.Alan()
	}
	return
}

func main() {
	fmt.Println("-- ayni is, uc yazim --")

	karisik := []any{Kare{Kenar: 2}, Daire{YariCap: 1}, Kare{Kenar: 3}}
	tAny, iddia := toplamAlanAny(karisik)
	fmt.Printf("bos arayuz    toplam=%.4f  tip iddiasi=%d  bildirim satiri=0\n", tAny, iddia)

	homojen := []Kare{{Kenar: 2}, {Kenar: 3}}
	tGenel := toplamAlanGenel(homojen)
	fmt.Printf("genellik      toplam=%.4f  tip iddiasi=0  bildirim satiri=1 (kisit)\n", tGenel)

	sozlesmeli := []Sekil{Kare{Kenar: 2}, Daire{YariCap: 1}, Kare{Kenar: 3}}
	tArayuz := toplamAlanArayuz(sozlesmeli)
	fmt.Printf("arayuz        toplam=%.4f  tip iddiasi=0  bildirim satiri=1 (arayuz)\n", tArayuz)

	fmt.Println()
	fmt.Println("-- sinirlayici olcum: T bir arayuze sabitlenince kazanc kayboluyor --")
	heterojen := []Sekil{Kare{Kenar: 2}, Daire{YariCap: 1}}
	tDejenere := toplamAlanGenel(heterojen)
	fmt.Printf("toplamAlanGenel[Kare](homojen)    T=Kare   eleman boyutu=%d bayt  sonuc=%.4f\n",
		unsafe.Sizeof(homojen[0]), tGenel)
	fmt.Printf("toplamAlanGenel[Sekil](heterojen) T=Sekil  eleman boyutu=%d bayt  sonuc=%.4f\n",
		unsafe.Sizeof(heterojen[0]), tDejenere)
}
```

```
-- ayni is, uc yazim --
bos arayuz    toplam=16.1416  tip iddiasi=3  bildirim satiri=0
genellik      toplam=13.0000  tip iddiasi=0  bildirim satiri=1 (kisit)
arayuz        toplam=16.1416  tip iddiasi=0  bildirim satiri=1 (arayuz)

-- sinirlayici olcum: T bir arayuze sabitlenince kazanc kayboluyor --
toplamAlanGenel[Kare](homojen)    T=Kare   eleman boyutu=8 bayt  sonuc=13.0000
toplamAlanGenel[Sekil](heterojen) T=Sekil  eleman boyutu=16 bayt  sonuc=7.1416
```

**GN13.** İlk üç satır aynı hesaplamanın üç kaynağını karşılaştırıyor. Boş arayüz yazımı
üç elemanın üçü için de bir tip iddiası (tip anahtarının bir dalı) çalıştırıyor ve hiçbir
arayüz bildirmiyor — sözleşme kaynakta hiçbir yerde yazılı değil, yalnız `switch` içindeki
`case` dalları örtük olarak hangi tiplerin bekleneceğini söylüyor. Genellik yazımı sıfır
tip iddiasıyla çalışıyor ama `homojen` yalnız `Kare` içeriyor — `T`, Genel Veri Yapıları
dersinin ölçtüğü gibi, çağıranın bildiriminde tek bir somut tipe sabitleniyor. Arayüz
yazımı da sıfır tip iddiasıyla çalışıyor ve üstelik `Kare` ile `Daire`yi **aynı dilimde**
taşıyabiliyor — `sozlesmeli` ikisini birden içeriyor, `homojen` içeremezdi.

#### Sınırlayıcı Ölçüm: T Bir Arayüze Sabitlenince Kazanç Kayboluyor

**GN14.** Son iki satır genelliğin sınırını gösteriyor. `toplamAlanGenel`, kısıtı bir
yöntem listesi olduğu için, `T`yi bir arayüz tipiyle de örnekleyebiliyor —
`toplamAlanGenel[Sekil](heterojen)` derleniyor ve `Kare` ile `Daire`yi karışık bir
dilimde kabul ediyor. Ama bunun bedeli ölçülüyor: `T=Kare` iken her eleman 8 bayt (yalnız
`Kenar` alanının kendisi), `T=Sekil` iken her eleman 16 bayt — arayüz çiftinin taşıdığı
tip işaretçisi ile veri işaretçisinin toplamı. `T`yi bir arayüze sabitlemek, genelliğin
sıfır maliyetli sözünü bozuyor: kod artık `toplamAlanArayuz`dan hiçbir farkı olmayan bir
şey çalıştırıyor, yalnız bunu bir tip parametresinin arkasına gizlemiş durumda.

`toplamAlanGenel`in gövdesi iki çağrıda da harfi harfine aynı kaldı — `for _, e := range d
{ toplam += e.Alan() }` satırı ne `T=Kare` ne `T=Sekil` için değişti. Değişen yalnız
`T`nin ne olduğu ve bu değişim eleman boyutunu iki katına çıkardı. Bu, genelliğin
tanımının kendisinden geliyor: bir genel gövde, tek bir kaynaktan, örneklendiği her `T`
için **aynı** işlemi çalıştırır. `T=Kare` iken bu işlem `Kare.Alan()`i doğrudan, ek bir
katman olmadan çağırıyor; `T=Sekil` iken aynı işlem artık bir arayüz değeri üzerinden
dolaylı bir yöntem çağrısı yapıyor — kaynakta hiçbir satır değişmese de, çalışma zamanında
olan biten değişiyor.

Bu, kursun sorusuna kesin bir yanıt veriyor: genellik her zaman kazanmaz. Bir koleksiyonun
bütün elemanları aynı somut tipteyse ve üzerlerinde yapılan işlem her tip için aynıysa,
genellik hem sıfır tip iddiası hem sıfır ek bayt sağlıyor. Elemanlar farklı somut tipler
taşımak zorundaysa — davranış tipe göre gerçekten değişmesi gerekiyorsa — iki seçenek
kalıyor: `T`yi bir arayüze sabitleyip genelliğin kazancından vazgeçmek, ya da baştan
arayüz yazmak. İkincisi daha dürüst bir yazım, çünkü kaynak zaten neyin olacağını —
birden fazla somut tipin aynı yerde duracağını — söylüyor. Genel bir gövde, tanımı
gereği, örneklendiği her `T` için **aynı** kalıyor; farklı `T`ler için farklı davranmasını
istemek, genelliğin kendisiyle çelişiyor.

#### Kurs Kapanışı

Arayüzler, Hatalar ve Genellikler kursu tek bir soruyla açıldı: Go'da bir tipin bir
arayüzü karşıladığı hiçbir yerde yazmıyor — öyleyse bu bağ nerede kuruluyor? On üç ders bu
soruyu üç düzenekte sordu: arayüzlerde (örtük gerçekleştirim, boş arayüz, tip iddiası,
gömme, tüketici tarafında tanımlama), hata değerlerinde (hata arayüzü, nöbetçi hatalar,
sarmalama, panik) ve genelliklerde (tip parametresi, tip kısıtı, genel veri yapıları ve
bu derste üçünün karşılaştırılması).

Birinci iddia doğrulandı: on altı bağın sekizi değerin içinde taşınıyor, çünkü Go'nun
arayüz değeri bir çift — hem somut tip hem veri. İkinci iddia doğrulandı: dört bağ
kullanım yerinde kuruluyor ve kaynak, bir tipin bir arayüzü karşıladığını hiçbir yerde
söylemiyor. Üçüncü iddia bu derste doğrudan koşturuldu: genellik ile arayüz aynı işi iki
ayrı yerde yapıyor — genellikte bağ bildirimde ve kullanım yerinde kalıyor, arayüzde
değere giriyor — ve genelliğin bu kazancı, tip parametresi bir arayüze sabitlendiği anda
kayboluyor.

Bu üç düzenekten biri, "kullanım yeri", bir uyarı taşıyor: kullanım yeri derleyicinin
baktığı yerdir, kaynağın işaret ettiği yer değildir. Genel Fonksiyonlar dersi bunu
doğrudan derleyiciye sorarak — kaynağı geçici bir pakete yazıp yalnız geçip geçmediğini
okuyarak — kanıtladı; Genel Veri Yapıları ve Tip Kısıtları dersleri de aynı yöntemi
kullandı. Dolaylı bir kanıt (yalnız kaynakta bir sözleşmenin yazılı olmadığını göstermek)
ile doğrudan bir kanıt (derleyicinin nerede durduğunu koşturarak görmek) arasındaki bu
fark, kursun kendi ölçüm disiplininin bir parçası: bir ders "kullanım yerinde kuruluyor"
diyorsa, ya derleyiciyi çağırıp nerede durduğunu göstermeli, ya da kanıtın dolaylı
olduğunu açıkça yazmalıdır.

Kurs aşırı bir genelleme yapmadan kapanıyor: bu üç düzenek (bildirim, kullanım yeri, değer) Go'nun
bütün tip sistemini kapsamıyor, yalnız on üç dersin sorduğu tek soruya — bu bağ nerede kuruluyor?
— verilen yanıtları sınıflıyor. Aşağıdaki tablo bu on üç yanıtı tek satırda topluyor.

| Ders | Ölçülen bağ | Kurulduğu yer (bildirim · kullanım yeri · değer) | Sınırlayıcı ölçüm |
|---|---|---|---|
| Arayüz Kavramı | Bir tipin bir arayüzü örtük karşılaması; küçük arayüz ilkesi | kullanım yeri (4/4/8 dağılımı burada üretildi) | Derleme zamanı uyum kanıtı bağı bildirime taşır, kanıt yokken derleyici sessiz kalır |
| Boş Arayüz ve `any` | Boş arayüzün tip bilgisini değere taşıması | değer | `nil` işaretçili arayüz `nil` değildir |
| Tip İddiası ve Tip Anahtarı | Değere sorma: iki yanıtlı iddia, tip anahtarının seçtiği dal | değer | Aynı değer hem somut hem arayüz dalına uyabilir, seçilen dal yazılış sırasına bağlı |
| Arayüz Gömme | Sözleşme bileşimi | bildirim | Gömme yalnız sözleşmeyi birleştirir, gerçekleştirimi birleştirmez |
| Arayüz Tasarımı | Aynı somut tip, tüketicilerin tanımladığı dört bağımsız arayüzü karşılıyor; kaynağında sıfır atıf var | kullanım yeri | Aynı imzalı iki ayrı adlı arayüz örtük eşdeğer olur; eşdeğerlik ancak imza ayrılınca kırılır |
| Hata Arayüzü | Hatanın örtük karşılanan bir değer olması | kullanım yeri + değer | `nil` olmayan arayüz `nil` somut değer taşıyabilir |
| Nöbetçi Hatalar | Hata kimliğinin değerde durması | değer | Kimlik değişince çağıran sessizce yanlış dala düşer, uyarı üretilmez |
| Hata Sarmalama ve Çözme | Zincirin değerde taşınması | değer | `%v` kullanmak zinciri koparır, metin aynı kalır |
| Panik ve Kurtarma | Paniğin imzada hiçbir iz bırakmaması | (bağ kurulmuyor) | Kurtarma paniği yok etmez, taşır; kurtarılan değer yazılmazsa bilgi tümüyle kaybolur |
| Genel Fonksiyonlar | Tip parametresinin çağrı yerinde çıkarılması | kullanım yeri | Kısıtsız `T` ile `==` ve `+` yazılamaz |
| Tip Kısıtları | Kısıtın tip kümesi listelemesi | bildirim | Tip kümesi terimli arayüz yalnız kısıt konumunda geçerli |
| Genel Veri Yapıları | Örneklemenin çağıranın bildiriminde durması | bildirim | Karşılaştırılabilirlik kapsayıcının alan yapısına da bağlı |
| Ne Zaman Genellik | Aynı işin üç yazımda nerede bağlandığı | (üçünün karşılaştırması) | `T` bir arayüze sabitlenince genelliğin kazancı kayboluyor |

Kurs boyunca ölçülen üç düzenek, sonuçta, tek bir alışkanlığa iniyor: bir kaynağı okurken
"bu bağın nerede kurulduğu" sorusunu sormak. Bir bildirim satırı arandığında bulunuyorsa
bağ oradadır; bulunmuyorsa iki yer kalıyor — ya derleyicinin kullanım yerinde kurduğu bir
bağ (ki bunu görmenin tek yolu derleyiciyi çağırmaktır), ya da yalnız çalışan değere
sorularak öğrenilebilecek bir bağ. Bu üç soruyu sırayla sormak, Go'nun tip sistemini
okurken kaynağın söylediğiyle söylemediğini ayırmanın ölçülebilir bir yolu.

#### Özet

- Aynı toplam alan hesabı üç yazımla kuruldu: boş arayüz üç tip iddiası gerektirdi,
  genellik ve arayüz sıfır tip iddiasıyla çalıştı.
- Genellik yalnız `T` tek bir somut tipe sabitlendiğinde sıfır maliyetli kalıyor; `T` bir
  arayüze sabitlenince eleman boyutu 8 bayttan 16 bayta çıkıyor ve kazanç kayboluyor.
- Davranış tipe göre gerçekten değişmesi gerekiyorsa arayüz gerekiyor, çünkü genel bir
  gövde örneklendiği her `T` için aynı kalıyor.
- Kurs boyunca ölçülen on altı bağın dördü bildirimde, dördü kullanım yerinde, sekizi
  değerin içinde kuruldu; bu dağılım Go'nun tip sistemini değil, yalnız bu on üç dersin
  sorduğu soruyu sınıflıyor.
- Genellik ile arayüz aynı işi iki ayrı yerde yapıyor: genellikte bağ değere hiç
  girmiyor, arayüzde giriyor — ve bu fark, ne zaman hangisinin seçileceğinin ölçülebilir
  karşılığı.

