---
title: 'Panik ve Kurtarma'
source: 'https://academia.sh/tr/kurslar/go-tip-sistemi/panik-ve-kurtarma'
course: 'Arayüzler, Hatalar ve Genellikler'
language: tr
updated: '2026-08-23T14:25:02+00:00'
license: 'CC BY-SA 4.0'
---

# 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("%-46s %-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.
