---
title: 'Hata Sarmalama ve Çözme'
source: 'https://academia.sh/tr/kurslar/go-tip-sistemi/hata-sarmalama-ve-cozme'
course: 'Arayüzler, Hatalar ve Genellikler'
language: tr
updated: '2026-08-23T14:25:01+00:00'
license: 'CC BY-SA 4.0'
---

# Hata Sarmalama ve Çözme

%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?
