Go'da tip iddiası (type assertion) x.(T), arayüz tipindeki x değişkeninin içinde gerçekten T tipinde bir değer olduğunu öne sürer ve o değeri T tipiyle geri verir. İddia yanlışsa program paniğe düşer; bunu önlemek için iki değerli v, ok := x.(T) biçimi kullanılır. Type switch ise switch v := x.(type) yazımıyla aynı arayüz değerini birden çok tipe karşı sırayla dener ve eşleşen dalda v'yi o tipte kullanmanızı sağlar.
Bu yazıda tip iddiasının iki biçimini, somut tip ile arayüz tipine yapılan iddia arasındaki farkı, type switch kurallarını (nil durumu, çoklu tip dalları, sıra, fallthrough yasağı), isteğe bağlı yetenek desenini ve generic kodda tip iddiası kullanmayı Go 1.27.3 ile çalıştırılmış örneklerle anlatıyoruz. Arayüzlerin temeli için Go interface yazısına, serinin tamamı için Go öğrenme rehberi sayfasına bakabilirsiniz.
Tip iddiası: x.(T)

Spesifikasyona göre x.(T) iki şeyi birden öne sürer: x nil değildir ve içinde saklanan değer T tipindedir. Kural T'nin ne olduğuna göre değişir. T arayüz olmayan (somut) bir tipse, x'in dinamik tipinin T ile birebir aynı olması gerekir. T bir arayüz tipiyse, x'in dinamik tipinin T arayüzünü uygulaması yeterlidir. İddia tutarsa ifadenin değeri, x'in içindeki değerdir ve tipi T'dir; tutmazsa çalışma zamanı paniği oluşur.
package main
import "fmt"
func main() {
var x any = "merhaba"
s := x.(string)
fmt.Println(s, len(s))
n, ok := x.(int)
fmt.Println(n, ok)
f, ok := x.(fmt.Stringer)
fmt.Println(f, ok)
n = x.(int)
}
// Çıktı:
// merhaba 7
// 0 false
// <nil> false
// panic: interface conversion: interface {} is string, not intİkinci satırda iddia tutmadığı için n int'in sıfır değeri olan 0'ı, ok ise false'u aldı ve panik oluşmadı. Üçüncü satır arayüz tipine yapılan bir iddiadır: string tipinin String() metodu olmadığı için fmt.Stringer iddiası başarısız oldu ve f nil arayüz değerini aldı. Son satırdaki tek değerli iddia ise paniğe yol açtı. Panik mesajı, içeride gerçekte ne olduğunu (string) ve ne beklendiğini (int) söylediği için hata ayıklarken işe yarar.
Derleyicinin reddettiği iddialar
Somut bir T tipine yapılan iddiada T, x'in arayüz tipini uygulamak zorundadır; aksi halde x'in içinde hiçbir zaman T bulunamayacağı için iddia anlamsızdır ve derleme hatası verir:
package main
type Sekil interface{ Alan() float64 }
type Kare struct{ K float64 }
func (k Kare) Alan() float64 { return k.K * k.K }
func main() {
var s Sekil = Kare{2}
_ = s.(string)
}# command-line-arguments
./main.go:11:6: impossible type assertion: s.(string)
string does not implement Sekil (missing method Alan)Ayrıca tip iddiası yalnızca arayüz tipindeki ifadelere uygulanabilir. var n int = 5; n.(int) gibi bir ifade derlenmez; somut tipler arasında geçiş için tip dönüşümü (float64(n)) kullanılır. Dönüşümlerin kuralları temel veri tipleri yazısında anlatılıyor.
Arayüz tipine iddia: isteğe bağlı yetenekler
Tip iddiasının en güçlü kullanımlarından biri, bir değerin elinizdeki arayüzden fazlasını yapıp yapamadığını sormaktır. Effective Go, encoding/json paketinin bir değerin json.Marshaler uygulayıp uygulamadığını çalışma zamanında val.(json.Marshaler) ile kontrol ettiğini örnek verir. Aşağıda io.Reader alan bir fonksiyon, okuyucunun ayrıca bir adı olup olmadığına bakıyor:
package main
import (
"fmt"
"io"
"strings"
)
// Isimli, isteğe bağlı bir yetenek: bazı okuyucuların adı vardır.
type Isimli interface {
Ad() string
}
type adliOkuyucu struct {
io.Reader
ad string
}
func (a adliOkuyucu) Ad() string { return a.ad }
func oku(r io.Reader) {
veri, _ := io.ReadAll(r)
ad := "adsız"
if i, ok := r.(Isimli); ok {
ad = i.Ad()
}
fmt.Printf("%s: %q\n", ad, veri)
}
func main() {
oku(strings.NewReader("düz metin"))
oku(adliOkuyucu{strings.NewReader("ayarlar"), "config.txt"})
}
// Çıktı:
// adsız: "düz metin"
// config.txt: "ayarlar"Yalnızca bir yeteneğin var olup olmadığını öğrenmek ve değeri kullanmamak istiyorsanız Effective Go'daki gibi boş tanımlayıcı kullanın: if _, ok := val.(json.Marshaler); ok { ... }. Bu biçim, bir değerin belirli bir arayüzü uygulayıp uygulamadığını hata mesajında bildirmek gibi durumlar için yeterlidir ve gereksiz bir değişken tanımlamaz.
adliOkuyucu, io.Reader'ı gömdüğü için Read metodunu ondan alıyor ve ek olarak Ad tanımlıyor. oku fonksiyonunun imzası yalnızca io.Reader istediği için her okuyucuyla çalışmaya devam ediyor, ama ek yetenek varsa onu kullanıyor. Bu kodda io.ReadAll'ın hatasını örnek kısa kalsın diye yok saydık; gerçek kodda hatayı kontrol etmelisiniz (bkz. Go error yazısı).
Type switch

Aynı değeri birden çok tipe karşı denemek gerektiğinde art arda if v, ok := x.(T) yazmak yerine type switch kullanılır. Spesifikasyona göre type switch değerleri değil tipleri karşılaştırır ve özel bir koruma ifadesi (guard) kullanır: x.(type). Bu yazım yalnızca switch içinde geçerlidir. Koruma ifadesinde kısa değişken bildirimi (v := x.(type)) kullanıldığında her dalda yeni bir v tanımlanır ve tipi dala göre değişir:
package main
import (
"fmt"
"time"
)
func tanimla(x any) string {
switch v := x.(type) {
case nil:
return "nil değer"
case int:
return fmt.Sprintf("int, iki katı %d", v*2)
case string:
return fmt.Sprintf("string, %d bayt", len(v))
case bool, float64:
return fmt.Sprintf("bool ya da float64: %v (%T)", v, v)
case []int:
return fmt.Sprintf("int dilimi, %d eleman", len(v))
case fmt.Stringer:
return "Stringer: " + v.String()
case error:
return "error: " + v.Error()
default:
return fmt.Sprintf("bilinmeyen tip %T", v)
}
}
func main() {
degerler := []any{nil, 21, "çay", true, 2.5, []int{1, 2, 3},
3 * time.Second, fmt.Errorf("bağlantı koptu"), map[string]int{}}
for _, d := range degerler {
fmt.Println(tanimla(d))
}
}
// Çıktı:
// nil değer
// int, iki katı 42
// string, 4 bayt
// bool ya da float64: true (bool)
// bool ya da float64: 2.5 (float64)
// int dilimi, 3 eleman
// Stringer: 3s
// error: bağlantı koptu
// bilinmeyen tip map[string]intBu örnek type switch'in bütün kurallarını gösteriyor:
- Tek tipli dal:
case int:dalındav'nin tipiint'tir; bu yüzdenv*2yazılabilir. - Çok tipli dal:
case bool, float64:dalındav'nin tipi, koruma ifadesindeki ifadenin tipi, yanianyolarak kalır.%Tyine gerçek dinamik tipi yazdırır amavüzerinde sayı işlemi yapamazsınız. - nil dalı:
case nil:yalnızcaxnil arayüz değeri olduğunda seçilir ve en fazla bir kez yazılabilir. Unutmayın: nil bir*Tişaretçisi taşıyan arayüz nil değildir,case *Tdalına düşer. - Arayüz dalları:
case fmt.Stringer:gibi arayüz tipleri, dinamik tip o arayüzü uyguluyorsa eşleşir.time.DurationbirStringmetoduna sahip olduğu için "3s" bu dala düştü. - default: Hiçbir dal eşleşmezse çalışır;
vyineanytipindedir. - fallthrough yok: Spesifikasyon type switch içinde
fallthroughdeyimine izin vermez. Normal switch'teki davranış için if ve switch yazısına bakabilirsiniz.
Spesifikasyon type switch'i, her dalı bir if ... else if zinciriyle yeniden yazarak açıklar ve koruma ifadesinin yalnızca bir kez değerlendirildiğini belirtir. Bir dalda listelenen her somut tip, x'in arayüz tipini uygulamak zorundadır ve aynı tip iki dalda tekrarlanamaz.
Type switch öncesinde kısa deyim
if ve normal switch deyimlerinde olduğu gibi, type switch koruma ifadesinden önce de noktalı virgülle ayrılmış basit bir deyim yazılabilir: switch v := sonuc(); t := v.(type) { ... }. Spesifikasyona göre bu deyim koruma ifadesi değerlendirilmeden önce çalışır ve tanımladığı değişkenler yalnızca switch bloğunda görünür. Bu biçim, bir fonksiyonun döndürdüğü arayüz değerini dış kapsama sızdırmadan sınıflandırmak için kullanışlıdır.
Dal sayısı arttıkça okunabilirliği korumak için her dalı kısa tutun. Bir dalda uzun bir iş yapılıyorsa o işi ayrı bir fonksiyona taşıyıp dalda yalnızca çağırmak hem testi hem bakımı kolaylaştırır. Değerlerin çoğu aynı türden bir işlem görüyorsa type switch yerine o tiplerin ortak bir metodunu isteyen bir arayüz tanımlamayı düşünün; dallanmak yerine metot çağırmak Go'da genellikle daha doğal bir tasarımdır.
Dalların sırası önemlidir
Type switch dalları yukarıdan aşağıya denenir ve ilk eşleşen dal çalışır. Bir arayüz dalı, o arayüzü uygulayan somut tiplerin dallarından önce gelirse onları gölgeler:
package main
import (
"fmt"
"time"
)
func sira1(x any) string {
switch x.(type) {
case fmt.Stringer:
return "Stringer"
case time.Duration:
return "Duration"
}
return "diğer"
}
func sira2(x any) string {
switch x.(type) {
case time.Duration:
return "Duration"
case fmt.Stringer:
return "Stringer"
}
return "diğer"
}
func main() {
d := 90 * time.Second
fmt.Println(sira1(d), sira2(d))
}
// Çıktı:
// Stringer DurationBu örnekte değişken kullanmadığımız için switch x.(type) yazdık; dalda değere ihtiyacınız yoksa kısa bildirimi atlayabilirsiniz. Kural basit: daha özel olan somut tipleri üste, genel arayüz dallarını alta yazın. Effective Go'nun anlattığı gibi fmt.Printf da bir değeri metne çevirirken önce string olup olmadığına, sonra Stringer uygulayıp uygulamadığına bu tür bir type switch ile bakar.
Generic kodda tip iddiası
Spesifikasyon, tip iddiası ve type switch için x'in arayüz tipinde olmasını ama tip parametresi olmamasını şart koşar. Bu yüzden bir generic fonksiyonda T tipindeki bir değere doğrudan type switch uygulanamaz:
package main
import "fmt"
func Ad[T any](v T) string {
switch v.(type) {
case string:
return "metin"
}
return "diğer"
}
func main() { fmt.Println(Ad(1)) }# command-line-arguments
./main.go:6:9: cannot use type switch on type parameter value v (variable of type T constrained by any)Değeri önce any'ye dönüştürürseniz derleyici kabul eder; çünkü any(v) artık sıradan bir arayüz değeridir:
package main
import "fmt"
func Ad[T any](v T) string {
switch any(v).(type) {
case string:
return "metin"
case int:
return "tam sayı"
}
return "diğer"
}
func main() {
fmt.Println(Ad("a"), Ad(7), Ad(1.5))
}
// Çıktı:
// metin tam sayı diğerBu teknik çalışır, ama generic bir fonksiyonun gövdesinde tip parametresine göre dallanıyorsanız fonksiyonun aslında generic olmaması gerekebilir. Go Blog'daki "When To Use Generics" yazısı, her tip için farklı davranış gerekiyorsa tip parametresi yerine arayüz ve farklı metot uygulamaları kullanmayı önerir. Spesifikasyon ayrıca bir type switch dalında tip parametresi (case P:) kullanılmasına izin verir; örnekleme sonrası iki dal aynı tipe denk gelirse ilk eşleşen dal seçilir. Generics'in temeli için Go generics yazısına bakın.
Hata ayrıntısına tip iddiasıyla bakmak
Effective Go, hata ayrıntısına ihtiyaç duyan çağıranların tip iddiası veya type switch ile belirli hata tiplerini arayabileceğini gösterir. Belgedeki örnekte os.Create başarısız olduğunda, hatanın *os.PathError olup olmadığına ve içindeki Err alanının disk dolu anlamına gelen syscall.ENOSPC olup olmadığına bakılır; öyleyse geçici dosyalar silinip işlem bir kez daha denenir. İddia ve alan kontrolü tek koşulda birleştirilir: if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOSPC. İddia başarısız olursa ok false, e nil olur ve && kısa devre yaptığı için e.Err hiç değerlendirilmez.
Hatalar için tip iddiası yerine errors.AsType
Bir hatanın belirli bir tipte olup olmadığını err.(*fs.PathError) ile sormak, hata fmt.Errorf("...: %w", err) ile sarmalanmışsa başarısız olur, çünkü dış hata farklı bir tiptir. errors paket belgesi bu yüzden errors.AsType[*fs.PathError](err) biçimini önerir; bu fonksiyon hata zincirini dolaşarak eşleşen tipi bulur. AsType Go 1.26'da eklendi; ayrıntılar ve örnekler Go error yazısında.
Biçimlerin karşılaştırması

| Yazım | Başarısız olursa | Ne zaman |
|---|---|---|
| v := x.(T) | panik | Tipin T olduğundan emin olduğunuzda |
| v, ok := x.(T) | v sıfır değer, ok false | Tip belirsizken tek tipi denerken |
| switch v := x.(type) | default dalı ya da hiçbir şey | Birden çok tipe göre dallanırken |
| x.(Arayuz) | panik ya da ok false | Değerin ek bir yeteneği olup olmadığını sorarken |
| errors.AsType[T](err) | sıfır değer, false | Sarmalanmış hatalarda tip ararken |
Sık yapılan hatalar
- Tek değerli iddiayı kontrolsüz kullanmak: Kullanıcı girdisi, JSON'dan gelen
anydeğerleri gibi belirsiz verilerdex.(T)panik riskidir;v, okbiçimini kullanın. - Çok tipli dalda değişkenin tipini somut sanmak:
case int, int64:dalındavhâlâ arayüz tipindedir,v + 1derlenmez. - Arayüz dalını somut dallardan önce yazmak: Somut dallar hiçbir zaman çalışmaz; derleyici bunun için uyarı vermez.
- nil işaretçiyi case nil ile yakalamayı beklemek: İçinde nil
*Tolan arayüz değericase *Tdalına düşer. - Sarmalanmış hatalarda doğrudan tip iddiası:
errors.AsTypeveyaerrors.Askullanın. - Tip parametresine doğrudan iddia: Derlenmez; gerekiyorsa
any(v)ile dönüştürün, ama önce tasarımı gözden geçirin.
Sık Sorulan Sorular
Tip iddiası ile tip dönüşümü arasındaki fark nedir?
Tip dönüşümü (float64(n)) derleme anında bilinen bir tipten başka bir tipe geçiştir ve somut tipler arasında yapılır. Tip iddiası (x.(T)) ise bir arayüz değerinin içindeki dinamik tipi çalışma zamanında kontrol eder ve değeri çıkarır. Effective Go type switch'leri bir tür dönüşüm olarak görür ama mekanizma farklıdır.
Başarısız iddiada değişken ne değer alır?
İki değerli biçimde ok false olur ve ilk değişken T'nin sıfır değerini alır: sayılar için 0, string için boş metin, işaretçi ve arayüzler için nil. Panik oluşmaz.
Type switch'te fallthrough kullanabilir miyim?
Hayır. Spesifikasyon type switch içinde fallthrough'a izin vermez. Birden çok tip için aynı kodu çalıştırmak istiyorsanız onları tek dalda virgülle listeleyin: case int, int64:.
Aynı tipi iki dalda yazabilir miyim?
Hayır. Spesifikasyona göre bir type switch'in dallarında listelenen tipler birbirinden farklı olmalıdır. Bir somut tip ile onun uyguladığı bir arayüz farklı tipler sayılır, bu yüzden ikisi ayrı dallarda bulunabilir; o durumda yukarıda gösterildiği gibi ilk eşleşen dal kazanır.
Arayüzden başka bir arayüze iddia yapılabilir mi?
Evet. r.(io.Closer) gibi bir iddia, içerideki dinamik tipin io.Closer'ı da uygulayıp uygulamadığını sorar. Spesifikasyonun örneğinde olduğu gibi başarılı olursa dinamik tip her iki arayüzü de uyguluyordur.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Go öğrenme rehberi · Go interface · Go generics · Go hata yönetimi · Go if ve switch · Go sık hatalar