Go'da metot, bir alıcısı (receiver) olan fonksiyondur: func (s Sayac) Goster() string yazdığınızda Goster metodu Sayac tipine bağlanır ve s.Goster() biçiminde çağrılır. Alıcı iki türlü olabilir: değer alıcı (s Sayac) metoda değerin bir kopyasını verir, işaretçi alıcı (s *Sayac) ise asıl değişkeni gösterir ve metodun yaptığı değişiklikler çağırana yansır. Hangi metotların hangi tipte çağrılabileceğini ve bir tipin hangi arayüzü uyguladığını metot kümesi (method set) kuralı belirler.
Bu yazıda metot tanımlamanın kurallarını, değer ve işaretçi alıcı arasındaki seçimi, metot kümelerini, derleyicinin otomatik eklediği & ve * işlemlerini, metot değerleri ile metot ifadelerini Go 1.27.3 ile çalıştırılmış örneklerle anlatıyoruz. Go'nun genel tanıtımı için Go nedir yazısına, tüm seri için Go öğrenme rehberi sayfasına bakabilirsiniz. Struct tanımlama ve gömme konusu Go struct yazısında, işaretçilerin kendisi ise Go pointer yazısında ayrıntılı olarak anlatılıyor.
Metot tanımı ve alıcı

Dil spesifikasyonuna göre alıcı, metot adından önce gelen ayrı bir parametre bölümüdür ve tam olarak bir parametre içermelidir; bu parametre variadic (...) olamaz. Alıcının tipi tanımlı bir tip T ya da onun işaretçisi *T olmalıdır. Bu T'ye metodun temel tipi (base type) denir. Temel tip kendisi bir işaretçi veya arayüz tipi olamaz ve metotla aynı pakette tanımlanmış olmalıdır. Alıcı adını metot gövdesinde kullanmıyorsanız atlayabilirsiniz; normal parametrelerde olduğu gibi.
Aşağıdaki örnekte aynı Sayac tipine biri değer, biri işaretçi alıcılı iki artırma metodu yazdık. Değer alıcılı ArtirKopya yalnızca kendi kopyasını değiştirdiği için çağıranın gördüğü değer sıfırda kalıyor; işaretçi alıcılı Artir ise gerçek değişkeni değiştiriyor.
package main
import "fmt"
type Sayac struct {
ad string
deger int
}
// Değer alıcı: s, çağıranın bir kopyasıdır.
func (s Sayac) ArtirKopya() {
s.deger++
}
// İşaretçi alıcı: s, çağıranın kendisini gösterir.
func (s *Sayac) Artir() {
s.deger++
}
func (s Sayac) Goster() string {
return fmt.Sprintf("%s=%d", s.ad, s.deger)
}
func main() {
z := Sayac{ad: "ziyaret"}
z.ArtirKopya()
fmt.Println(z.Goster())
z.Artir() // derleyici bunu (&z).Artir() olarak yazar
z.Artir()
fmt.Println(z.Goster())
p := &z
fmt.Println(p.Goster()) // (*p).Goster()
}
// Çıktı:
// ziyaret=0
// ziyaret=2
// ziyaret=2Dikkat ederseniz z bir işaretçi değil, düz bir Sayac değişkeni; buna rağmen z.Artir() çalışıyor. Spesifikasyondaki çağrı kuralı şöyle: x adreslenebilir (addressable) bir değişkense ve &x'in metot kümesinde m varsa, x.m() ifadesi (&x).m() için bir kısaltmadır. Ters yönde de benzer bir kolaylık var: p bir *Sayac olduğunda p.Goster() çağrısı, değer alıcılı metoda *p'nin kopyası verilerek yapılır.
Metotlar yalnızca struct'lara ait değildir
Effective Go'nun vurguladığı gibi, işaretçi ve arayüz dışındaki her tanımlı tipe metot ekleyebilirsiniz. Alıcının struct olması gerekmez: float64 tabanlı bir sıcaklık tipi, bir dilim tipi, hatta bir fonksiyon tipi metot taşıyabilir. Standart kütüphanedeki http.HandlerFunc bunun bilinen örneğidir; bir fonksiyon tipine ServeHTTP metodu eklenerek sıradan fonksiyonların HTTP işleyicisi olarak kullanılması sağlanır (ayrıntısı net/http sunucu yazısında).
package main
import (
"fmt"
"strings"
)
type Celsius float64
func (c Celsius) Fahrenheit() float64 {
return float64(c)*9/5 + 32
}
func (c Celsius) String() string {
return fmt.Sprintf("%.1f°C", float64(c))
}
type Etiketler []string
func (e Etiketler) Birlestir() string {
return strings.Join(e, ", ")
}
func (e *Etiketler) Ekle(s string) {
*e = append(*e, s)
}
func main() {
hava := Celsius(21.5)
fmt.Println(hava, hava.Fahrenheit())
var e Etiketler
e.Ekle("go")
e.Ekle("metot")
fmt.Println(len(e), e.Birlestir())
}
// Çıktı:
// 21.5°C 70.7
// 2 go, metotBurada iki ayrıntı önemli. Birincisi, Celsius bir String() string metodu tanımladığı için fmt.Println onu bu metotla yazdırıyor; bu davranış arayüzler yazısında anlatılan fmt.Stringer arayüzünden geliyor. İkincisi, Ekle metodu dilimin uzunluğunu değiştirdiği için işaretçi alıcı kullanıyor. Go FAQ bunu açıkça belirtir: dilimler ve map'ler referans gibi davransa da bir metot içinde dilimin uzunluğunu değiştirmek istiyorsanız alıcının yine işaretçi olması gerekir. Değer alıcılı bir Ekle yazsaydınız append sonucu yalnızca kopyaya atanır ve len(e) sıfır kalırdı.
Başka paketin tipine metot eklenemez
Temel tipin metotla aynı pakette olması kuralı, int veya time.Duration gibi başka bir pakette (ya da evrende) tanımlı tiplere doğrudan metot eklemenizi engeller. Derleyici şu hatayı verir:
package main
func (i int) Cift() int { return i * 2 }
func main() {}# command-line-arguments
./main.go:3:9: cannot define new methods on non-local type intÇözüm, kendi paketinizde yeni bir tip tanımlamaktır: type Sayi int yazıp metodu Sayi üzerine koyarsınız. Yeni tipin alttaki tipi (underlying type) int olduğundan Sayi(5) ve int(s) dönüşümleri serbesttir, ama Sayi artık int'in değil kendi metot kümesine sahiptir. Bir tür takma adıyla (type Sayi = int) bu yapılamaz, çünkü takma ad yeni bir tip oluşturmaz, yine int'i ifade eder.
Metot kümeleri: T ile *T neden farklı?

Spesifikasyon metot kümesini şöyle tanımlar: tanımlı bir T tipinin metot kümesi, alıcısı T olan bütün metotlardır. *T işaretçi tipinin metot kümesi ise alıcısı *T veya T olan bütün metotları içerir. Yani *T'nin kümesi T'ninkini kapsar, tersi geçerli değildir.
| Tanımlanan alıcı | T değerinin metot kümesinde mi? | *T değerinin metot kümesinde mi? |
|---|---|---|
| func (t T) M() | Evet | Evet |
| func (t *T) M() | Hayır | Evet |
Adreslenebilir bir değişkende z.Artir() çalıştığı için bu fark günlük kodda pek hissedilmez. Asıl önemli olduğu yer arayüzlerdir: bir arayüz değişkenine T değeri koyduğunuzda Go, işaretçi alıcılı metotları çağırabilmek için bu değerin adresini alamaz. FAQ'ın açıklaması şöyle: arayüz bir *T tutuyorsa metot, işaretçiyi izleyerek değere ulaşabilir; ama arayüz bir T değeri tutuyorsa metodun güvenli biçimde bir işaretçi elde etmesinin yolu yoktur. Böyle bir işaretçi, arayüzün içindeki değerin değiştirilmesine izin verirdi ve dil spesifikasyonu buna izin vermez. Sonuç derleme hatasıdır:
package main
import "fmt"
type Artirici interface {
Artir()
}
type Sayac struct{ n int }
func (s *Sayac) Artir() { s.n++ }
func main() {
var s Sayac
var a Artirici = s
a.Artir()
fmt.Println(s.n)
}# command-line-arguments
./main.go:15:19: cannot use s (variable of struct type Sayac) as Artirici value in variable declaration: Sayac does not implement Artirici (method Artir has pointer receiver)Hata mesajı sorunu doğrudan söylüyor: Artir işaretçi alıcılı olduğu için Sayac değeri arayüzü uygulamıyor. Düzeltmek için var a Artirici = &s yazarsınız; bu durumda a.Artir() asıl s'yi artırır ve s.n 1 olur. Effective Go'daki ByteSlice örneği de aynı nedenle fmt.Fprintf(&b, ...) biçiminde adres geçirir: io.Writer'ı yalnızca *ByteSlice uygular.
Adreslenemeyen değerlerde işaretçi metotları
Otomatik & eklemesi yalnızca adreslenebilir değerler için geçerlidir. Map elemanları ve fonksiyon dönüş değerleri ya da bir composite literal'in kendisi adreslenebilir değildir; bu yüzden üzerlerinde doğrudan işaretçi alıcılı metot çağrılamaz:
package main
type Sayac struct{ n int }
func (s *Sayac) Artir() { s.n++ }
func main() {
m := map[string]Sayac{"a": {}}
m["a"].Artir()
Sayac{}.Artir()
}# command-line-arguments
./main.go:9:9: cannot call pointer method Artir on Sayac
./main.go:10:10: cannot call pointer method Artir on SayacMap örneğinde çözüm, değeri bir değişkene alıp değiştirmek ve geri yazmak ya da map'i baştan map[string]*Sayac olarak tutmaktır. Literal örneğinde ise (&Sayac{}).Artir() yazabilirsiniz; spesifikasyon composite literal'lerin adresinin alınmasına özel olarak izin verir. Map'lerin ayrıntıları için Go map yazısına bakabilirsiniz.
nil işaretçi alıcı da geçerli bir alıcıdır
İşaretçi alıcılı bir metodu nil bir işaretçi üzerinden çağırmak kendi başına panik üretmez; panik ancak metot içinde nil işaretçinin bir alanına erişildiğinde oluşur. Bu özellik, bağlı liste veya ağaç gibi yapılarda nil'i "boş" anlamında kullanan metotlar yazmayı sağlar:
package main
import "fmt"
type Liste struct {
deger int
sonra *Liste
}
// nil alıcıyı bilerek ele alan bir metot.
func (l *Liste) Uzunluk() int {
if l == nil {
return 0
}
return 1 + l.sonra.Uzunluk()
}
func main() {
var bos *Liste
fmt.Println(bos.Uzunluk())
l := &Liste{1, &Liste{2, &Liste{3, nil}}}
fmt.Println(l.Uzunluk())
}
// Çıktı:
// 0
// 3Kontrolü kaldırırsanız bos.Uzunluk() çağrısı l.sonra okunurken nil işaretçi paniğine yol açar. Bu nedenle nil alıcıyı kabul eden metotların bunu belgelemesi iyi bir alışkanlıktır.
Metot değerleri ve metot ifadeleri
Bir metodu çağırmadan da kullanabilirsiniz. Metot değeri x.M biçimindedir ve alıcıyı o anda değerlendirip saklayan bir fonksiyon değeri üretir. Metot ifadesi T.M biçimindedir ve alıcıyı ilk argüman olarak alan sıradan bir fonksiyon verir. Spesifikasyon, metot değerinde alıcının değerlendirilip kopyasının saklandığını, sonraki çağrılarda bu kopyanın kullanıldığını açıkça söyler:
package main
import "fmt"
type Nokta struct{ X, Y int }
func (n Nokta) Topla(d int) int { return n.X + n.Y + d }
func (n *Nokta) Kaydir(dx int) { n.X += dx }
func main() {
n := Nokta{1, 2}
// Metot değeri: alıcı şimdi kopyalanıp saklanır.
f := n.Topla
n.X = 100
fmt.Println(f(0), n.Topla(0))
// Metot ifadesi: alıcı ilk argüman olur.
g := Nokta.Topla
fmt.Println(g(Nokta{3, 4}, 10))
h := (*Nokta).Kaydir
h(&n, 5)
fmt.Println(n.X)
fmt.Printf("%T\n%T\n", g, h)
}
// Çıktı:
// 3 102
// 17
// 105
// func(main.Nokta, int) int
// func(*main.Nokta, int)f oluşturulduğu anda n'nin kopyasını ({1, 2}) aldığı için sonradan n.X = 100 yapılması onu etkilemiyor ve sonuç 3 kalıyor. Metot ifadelerinde bir kısıt var: değer alıcılı bir metottan (*T).Mv ile işaretçi alan bir fonksiyon türetebilirsiniz, ama işaretçi alıcılı bir metottan T.Mp ile değer alan bir fonksiyon türetemezsiniz, çünkü Mp T'nin metot kümesinde değildir. Metot değerleri pratikte geri çağırma (callback) olarak sıkça kullanılır: slices.SortFunc(s, Tip.Compare) gibi bir çağrıda metot ifadesi doğrudan karşılaştırma fonksiyonu olur (bkz. slices, maps ve sort paketleri).
Metot alıcısı olarak kanal ve fonksiyon tipleri
Effective Go, aynı ServeHTTP metodunun bir struct'a, bir tam sayıya, bir kanala ve bir fonksiyona nasıl eklenebileceğini adım adım gösterir. Sayaç örneğinde önce type Counter struct{ n int } kullanılır, sonra bir struct'a gerek olmadığı, type Counter int tipinin yeterli olduğu ve artışın çağırana görünmesi için alıcının yine işaretçi olması gerektiği vurgulanır. Kanal örneğinde type Chan chan *http.Request tipinin metodu her ziyarette kanala bir değer gönderir. Bu örneklerin ortak dersi şudur: metot, verinin nasıl saklandığından bağımsız olarak bir davranış tanımlar; tipin altında struct, sayı, kanal veya fonksiyon olması fark etmez.
Aynı bölümde önemli bir uyarı da var: gerçek bir sunucuda ctr.n alanına aynı anda birden çok goroutine eriştiği için bu erişimin korunması gerekir. İşaretçi alıcılı bir metot paylaşılan veriyi değiştirir ve eşzamanlı kullanımda veri yarışına açıktır. Koruma yöntemleri için sync.Mutex ve WaitGroup yazısına bakabilirsiniz.
Değer mi, işaretçi mi? Karar listesi

Go FAQ, alıcı seçimini bir fonksiyon parametresini değer mi işaretçi mi alacağınız sorusuyla aynı görür ve şu ölçütleri verir:
- Metot alıcıyı değiştirecek mi? Değiştirecekse alıcı işaretçi olmak zorundadır. Bu en önemli ölçüttür.
- Verimlilik: Alıcı büyük bir struct ise her çağrıda kopyalamak yerine işaretçi kullanmak daha ucuz olabilir.
- Tutarlılık: Tipin bazı metotları işaretçi alıcılı olmak zorundaysa, metot kümesinin tipin nasıl kullanıldığından bağımsız tutarlı kalması için geri kalanını da işaretçi alıcılı yapmak FAQ'ın önerisidir.
- Küçük ve değişmez tipler: Temel tipler, küçük struct'lar ve dilim gibi tiplerde değer alıcı hem verimli hem de açıktır.
Bir struct'ın içine başka bir tipi gömdüğünüzde, gömülen tipin metotları dış tipin metot kümesine yükseltilir (promoted). Bu kuralın ayrıntıları ve gömülen alanın değer mi işaretçi mi olmasının metot kümesine etkisi struct yazısında anlatılıyor.
Generic tiplerde metotlar ve Go 1.27
Generic bir tipin metodunu yazarken alıcı, tipin tip parametrelerini yeniden bildirir: func (y *Yigin[T]) Ekle(v T). Parametre adlarının tip tanımındakilerle aynı olması gerekmez ve kısıtlar tip tanımından gelir. Go 1.27 sürüm notlarına göre artık bir metot bildirimi kendi tip parametrelerini de tanımlayabilir (generic metotlar); ancak arayüz metotları tip parametresi bildiremez ve arayüz metotları generic metotlarla uygulanamaz. Bu yeniliğin örnekleri Go 1.27 yenilikleri yazısında, generic tiplerin kendisi ise Go generics yazısında yer alıyor.
Sık yapılan hatalar
- Değer alıcıyla durum değiştirmeye çalışmak: Metot hata vermeden çalışır ama değişiklik kopyada kalır. Sayaç, ayar veya tampon gibi değişen veriler için işaretçi alıcı kullanın.
- Arayüze değer koymak: Metotlar
*Tüzerinde tanımlıysa arayüz değişkenineTdeğil&tatamanız gerekir; aksi halde "method has pointer receiver" hatası alırsınız. - Karışık alıcılar: Aynı tipte bazı metotları değer, bazılarını işaretçi alıcılı yazmak, tipin hangi arayüzü uyguladığını karıştırır. FAQ tutarlı olmayı önerir.
- Metot değerinin alıcıyı kopyaladığını unutmak:
f := n.Toplaanındaki değer saklanır;nsonra değişse defeski kopyayla çalışır. - Başka paketin tipine metot eklemeye çalışmak: Yeni bir tanımlı tip oluşturun; takma ad (
=) bu iş için çalışmaz. - Metot ve alan adı çakışması: Bir struct'ta aynı adlı alan ve metot olamaz; spesifikasyon bunu yasaklar.
Adalanı varkenAd()metodu tanımlayamazsınız.
Sık Sorulan Sorular
Go'da sınıf yok mu, metotlar nereye yazılır?
Go'da sınıf yoktur; metotlar herhangi bir tanımlı tipe, tip bildiriminden bağımsız olarak paket düzeyinde yazılır. Go FAQ'a göre Go nesne yönelimli bir programlama tarzına izin verir ama tip hiyerarşisi yoktur. Davranış paylaşımı arayüzler ve gömme ile yapılır.
Değer alıcılı metodu işaretçi üzerinden çağırabilir miyim?
Evet. *T'nin metot kümesi T'nin metotlarını da içerdiği için p.Goster() gibi çağrılar çalışır; Go işaretçiyi izleyip değerin kopyasını metoda verir. Tersi, yani işaretçi alıcılı metodu değer üzerinden çağırmak, yalnızca değer adreslenebilirse otomatik olarak yapılır.
Metotlar aşırı yüklenebilir (overloading) mi?
Hayır. Bir temel tipin metot adları benzersiz olmalıdır; aynı adla farklı parametreli iki metot yazamazsınız. FAQ bunun metot seçimini basitleştirmek için bilinçli bir karar olduğunu açıklar.
Metotlar dinamik olarak mı çağrılır?
Somut bir tip üzerindeki metot çağrıları her zaman derleme anında çözülür. FAQ'a göre dinamik dağıtımın (dynamic dispatch) tek yolu arayüzlerdir; arayüz değişkeni üzerinden yapılan çağrıda hangi metodun çalışacağı içindeki dinamik tipe göre belirlenir.
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 struct yapıları · Go pointer · Go generics · Go 1.27 yenilikleri