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

# Hata Arayüzü

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

Önceki Konu'nun İş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?
