---
title: Yapılar
source: 'https://academia.sh/tr/kurslar/go-temelleri/yapilar'
course: 'Go Temelleri'
language: tr
updated: '2026-08-23T14:25:00+00:00'
license: 'CC BY-SA 4.0'
---

# Yapılar

Yazılmayan alanların sıfır değeri, alanların sırayla yazılabilmesinin taşıdığı kırılganlık, ve gömmenin yazılmayan adları dış tipe taşıması ölçülüyor: bir gömülü tipin yöntemleri dış tipte hiç yazılmadan çağrılabiliyor. Sınırlayıcı ölçüm gömmenin kalıtım olmadığını gösteriyor: gömülü tipin yöntemi çağrıldığında alıcı gömülü değerin kendisidir, dış yapı değil.

Sözlüğün anahtarı bölünmez bir değerdi: tek bir dizgi ya da tam sayı, tek bir bilgi
taşıyordu. Yapı birden çok alanı bir arada tutan ve her alanına adıyla erişilen bir
bileşik tiptir. Sözlükle paylaştığı bir soru var — alanlara değer verilmezse ne olur — ama
yapının kendine özgü iki sorusu daha var: alanların **sırası** bir anlam taşıyor mu, ve bir
yapı başka bir yapının içine **gömüldüğünde** ne oluyor?

Bu iki soru, dizi ve dilim derslerinde kurulan "kopya mı, paylaşım mı" sorusundan ayrı bir
eksende duruyor. Diziler ve Dilimler dersi bir değerin ne kadarının kopyalandığını
ölçüyordu; yapı da bir değerdir ve aynı kurala uyuyor. Ama yapının kendine özgü sorusu
kopyalamayla ilgili değil, alanların **adlandırılmasıyla** ilgili: bir alanın adı, konumu
ve kaynağı — yazılıp yazılmadığı — birbirinden bağımsız üç ayrı bilgi taşıyor.

## Yazılmayan Alanların Sıfır Değeri

Bir yapı değişkeni ilk değeri verilmeden tanımlandığında, her alanı kendi tipinin sıfır
değerini alır — dizgi alanı boş dizgi, tam sayı alanı sıfır, mantıksal alan `false`. Bu,
Sıfır Değerler dersinin bağlayıcı 8/4/3 dağılımındaki **tanımlı** sınıfına giren
durumlardan biridir; burada tek cümleyle anılıyor, dağılımın tamamı yeniden kurulmuyor.

```go
// sifir.go — yazilmayan yapi alanlarinin sifir degeri
package main

import "fmt"

type Kayit struct {
	Ad    string
	Sayi  int
	Etkin bool
}

func main() {
	var bos Kayit
	fmt.Printf("bos kayit: %q %d %t\n", bos.Ad, bos.Sayi, bos.Etkin)
}
```

```
bos kayit: "" 0 false
```

Üç alan, üç ayrı tipin sıfır değerini taşıyor ve üçü de tek bir satırda, `var bos Kayit`
yazımından geliyor. Hiçbir alana açıkça değer verilmedi; yapının kendisi hangi alanın
hangi tipte olduğunu bildiği için, her birinin sıfır değerini kendisi dolduruyor. Bu, bir
yapı değişkeninin kurulması için ayrı bir yapıcı çağrısı gerekmediği anlamına geliyor —
`var bos Kayit` kendi başına kullanılabilir bir değer üretiyor, hiçbir alanı "henüz
kurulmamış" durumda bırakmıyor.

Bu doldurma gömülü tipler için de işliyor: bir yapı başka bir yapıyı gömüyorsa, gömülü
alanın kendisi de kendi sıfır değerini alıyor.

```go
// gomulu-sifir.go — gomulu tipin sifir degeri de kendiliginden doluyor
package main

import "fmt"

type Taban struct {
	Ad string
}

type Genisletilmis struct {
	Taban
	Sifre string
}

func main() {
	var g Genisletilmis
	fmt.Printf("bos genisletilmis: Ad=%q Sifre=%q\n", g.Ad, g.Sifre)
}
```

```
bos genisletilmis: Ad="" Sifre=""
```

`g.Ad` burada `Taban`ın alanı, `g.Sifre` `Genisletilmis`in kendi alanı; ikisi de tek bir
`var g Genisletilmis` satırından geldi. Sıfır değer doldurma iç içe geçmiş yapılarda da
duruyor, çünkü kural her düzeyde aynı: her alan kendi tipinin sıfır değerini alıyor,
alanın kendisi başka bir yapı olsa bile.

Yapı da tıpkı dizi gibi bir değerdir ve atandığında tamamen kopyalanır — bu ders boyunca
görülecek her yapı örneği bu kuralın altında duruyor. `kopya := kaynak` yazıldıktan sonra
`kopya` üzerinde yapılan bir değişiklik `kaynak`ı hiç etkilemiyor, çünkü iki değişken artık
birbirinden bağımsız iki kopya taşıyor. Bu davranış İşaretçiler dersinde asıl önemini
kazanacak: bir yapıyı değiştirmek isteyen bir işlev, kendisine bir kopya geçirildiğinde
çağırandaki hiçbir şeyi değiştiremiyor.

## Alanların Sırayla Yazılabilmesinin Kırılganlığı

Bir yapı değişmezi iki biçimde yazılabilir: alan adı yazmadan, yalnızca **sırayla**
(pozisyonel), ya da her değeri kendi alan adıyla eşleyerek (anahtarlı). Pozisyonel yazım
kısadır ama bir varsayıma dayanır — yazıldığı sıradaki alan dizilişinin, yapının **güncel**
tanımıyla aynı kalacağı varsayımına. Bu varsayım kırıldığında derleyici, iki alan aynı
tipteyse, hiçbir uyarı vermez.

```go
// siralama.go — pozisyonel yazim alan sirasina bagli, anahtarli yazim degil
package main

import "fmt"

type AdresV1 struct {
	Sehir string
	Ulke  string
}

type AdresV2 struct { // alan sirasi degisti: Ulke basa alindi
	Ulke  string
	Sehir string
}

func main() {
	v1 := AdresV1{"Ankara", "Turkiye"}
	v2 := AdresV2{"Ankara", "Turkiye"} // kod guncellenmedi, degerler V1'e gore yazildi
	fmt.Println("V1 pozisyonel :", "Sehir=", v1.Sehir, " Ulke=", v1.Ulke)
	fmt.Println("V2 pozisyonel :", "Ulke=", v2.Ulke, " Sehir=", v2.Sehir)
	v2Dogru := AdresV2{Sehir: "Ankara", Ulke: "Turkiye"}
	fmt.Println("V2 anahtarli  :", "Ulke=", v2Dogru.Ulke, " Sehir=", v2Dogru.Sehir)
}
```

```
V1 pozisyonel : Sehir= Ankara  Ulke= Turkiye
V2 pozisyonel : Ulke= Ankara  Sehir= Turkiye
V2 anahtarli  : Ulke= Turkiye  Sehir= Ankara
```

`AdresV2`, `AdresV1` ile aynı iki alanı taşıyor, yalnızca sırası değişmiş: `Ulke` başa
alınmış. `AdresV2{"Ankara", "Turkiye"}` satırı **hiç güncellenmeden** kalmış — hâlâ eski
sırayı (önce şehir, sonra ülke) varsayıyor. Derleyici burada hiçbir şeyi reddetmiyor, çünkü
iki alan da `string` tipinde; pozisyonel yazım yalnızca konuma bakıyor, adı hiç sormuyor.
Sonuç: `Ankara` şimdi `Ulke` alanına, `Turkiye` `Sehir` alanına yazılıyor — değerler yer
değiştirmiş. Anahtarlı yazım aynı iki değeri, hangi sırada yazılırsa yazılsın, doğru alana
koyuyor; `v2Dogru` alan sırasından tamamen bağımsız.

Bu kırılganlık yalnızca alan sırası **değiştiğinde** görünür oluyor, ve iki alan aynı
tipte olduğu sürece derleyici bunu asla yakalamıyor — reddedilen dört durumun (kullanılmayan
değişken, kullanılmayan içe aktarım, dönüşün yazılmaması, örtük tip dönüşümü) hiçbiri bu
durumu kapsamıyor. Alanların tipi **farklı** olsaydı pozisyonel yazım derlenmezdi ve hata
derleme zamanında görünürdü; tipin aynı kalması, hatayı çalışma zamanına, hatta hiçbir zaman
fark edilmemeye kadar erteliyor.

**BT11.** — `AdresV1` ve `AdresV2` burada bilinçli olarak iki alanlı ve iki alanı da aynı
tipte kuruldu; amaç, derleyicinin yakalayamayacağı en dar durumu göstermek. Üçüncü bir alan
farklı tipte eklenseydi pozisyonel yazım o alanda derlenmezdi ve kırılganlık görünmezdi.

Bu durum, gerçek bir kod tabanında bir yapının alan sırası **sonradan** değiştirildiğinde
ortaya çıkıyor: yapının tanımı güncellenir ama onu pozisyonel yazımla kuran her satır
otomatik olarak güncellenmiyor. Derleyici hiçbir uyarı vermediği için bu, çalıştırılıp
sonucu gözle kontrol edilmeden fark edilmesi zor bir kusur sınıfı oluyor — anahtarlı yazım
bu sınıfı tamamen ortadan kaldırıyor, çünkü hangi değerin hangi alana gittiği yazımın
kendisinde durur, alan sırasına hiç bağlı değildir.

## Karşılaştırılabilirlik Alanlardan Miras Kalır

Diziler ve Dilimler dersi bir dizinin `==` ile karşılaştırılabildiğini, bir dilimin
karşılaştırılamadığını ölçmüş ve bunun yapılara da yansıyacağını bir cümleyle bırakmıştı.
Bir yapının karşılaştırılabilir olup olmadığı, kendi kuralı değil, **alanlarının**
karşılaştırılabilirliğinin toplamıdır.

**BT12.** — Bu ölçüm bir derleyici çağrısıyla koşturuluyor ve yalnızca "derlendi" ya da
"derlenmedi" sınıfı basılıyor; iki tip tanımı da aynı görünen tek alanı taşıyor, farkı
yalnızca o alanın tipi belirliyor.

```go
// karsilastirma.go — yapinin karsilastirilabilirligi alanlarindan miras kalir
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func gecerMi(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
}

func main() {
	diziAlanli := `package sinama
	type K struct { Etiketler [2]string }
	func F() bool {
		a := K{[2]string{"x", "y"}}
		b := K{[2]string{"x", "y"}}
		return a == b
	}`
	dilimAlanli := `package sinama
	type K struct { Etiketler []string }
	func F() bool {
		a := K{[]string{"x", "y"}}
		b := K{[]string{"x", "y"}}
		return a == b
	}`
	fmt.Println("yalniz dizi alanli yapi:", gecerMi(diziAlanli))
	fmt.Println("dilim alanli yapi      :", gecerMi(dilimAlanli))
}
```

```
yalniz dizi alanli yapi: true
dilim alanli yapi      : false
```

İki yapı da tek bir alan taşıyor, ikisi de görünüşte aynı bilgiyi (iki etiket) tutuyor.
Fark alanın tipinde: biri dizi, öteki dilim. Dizi alanlı yapı `==` ile karşılaştırılabiliyor
çünkü dizi kendisi karşılaştırılabilir; dilim alanlı yapı karşılaştırılamıyor çünkü dilim
karşılaştırılamaz ve bu özellik yapıya **aynen** geçiyor. Yapı kendi başına bir
karşılaştırma kuralı tanımlamıyor — alanlarının kuralını devralıyor.

## Gömme: Yazılmayan Adların Dış Tipe Taşınması

Bir yapı, adlandırılmış bir alan yerine yalnızca başka bir tipin adını taşıyan bir alan
içerebilir; buna **gömme** deniyor. Gömülü tipin alanları ve yöntemleri, dış tipte hiç
yazılmadan, dış tip üzerinden doğrudan çağrılabilir hâle geliyor. Bu, tek bir alan ile
birden çok davranışın aynı anda dış tipe taşınmasını sağlıyor; aşağıdaki ölçümde `Taban`ın
iki yöntemi, `Genisletilmis` tarafında hiç yazılmadan görünüyor.

```go
// gomme.go — gomulu tipin adlari dis tipe yazilmadan tasinir
package main

import "fmt"

type Taban struct {
	Ad string
}

func (t Taban) Selamla() string {
	return "Merhaba, " + t.Ad
}

func (t Taban) KimKonusuyor() string {
	return fmt.Sprintf("%T diyor ki: %s", t, t.Ad)
}

type Genisletilmis struct {
	Taban
	Sifre string
}

func (g Genisletilmis) Dogrula(s string) bool {
	return g.Sifre == s
}

func main() {
	g := Genisletilmis{Taban: Taban{Ad: "Ayse"}, Sifre: "gizli"}
	fmt.Println("gomulu alan dogrudan   :", g.Ad)
	fmt.Println("gomulu yontem dogrudan :", g.Selamla())
	fmt.Println("kendi yontemi          :", g.Dogrula("gizli"))
}
```

```
gomulu alan dogrudan   : Ayse
gomulu yontem dogrudan : Merhaba, Ayse
kendi yontemi          : true
```

`Genisletilmis` yapısında `Ad` diye yazılmış bir alan yok, `Selamla` diye yazılmış bir
yöntem de yok — ikisi de `Taban`a ait. Ama `g.Ad` ve `g.Selamla()` çalışıyor, sanki
`Genisletilmis` bu ikisini kendisi tanımlamış gibi. `Taban`ın iki yöntemi (`Selamla`,
`KimKonusuyor`) `Genisletilmis`e **yazılmadan** taşındı; `Genisletilmis`in kendi yazdığı
tek yöntem `Dogrula`. Gömülü bir tipin adları, gömen tipin adlarıymış gibi görünüyor —
sözlükteki bir anahtarın aksine, burada erişim adla değil, doğrudan **alan seçimiyle**
oluyor.

**BT13.** — Taşınan yöntem sayısı burada ikidir (`Selamla`, `KimKonusuyor`); sayı `Taban`a
kaç yöntem yazıldığına bağlıdır ve gömen tipin kendi yazdığı yöntem sayısından bağımsızdır.
`Genisletilmis` sıfır yöntem yazsaydı bile ikisi yine görünür olurdu.

Gömme, bir yapının **birden çok** tipi gömmesiyle de çalışıyor ve taşınan adlar üst üste
birikir; iki gömülü tipin aynı adda bir alanı ya da yöntemi varsa, bu ders o çakışmayı
sınamıyor ama derleyici böyle bir çakışmada dış tipten doğrudan erişimi reddediyor — hangi
gömülü tipin kastedildiği açıkça yazılmak zorunda kalıyor. Bu ders yalnızca **tek** gömme
düzeyini ölçüyor; adı ve yöntemi çakışmayan tek bir gömülü tip.

## Sınırlayıcı Ölçüm: Gömme Kalıtım Değildir

Gömme, çağrı yazımı düzeyinde kalıtıma benziyor ama aynı şey değil. Fark, çağrılan
yöntemin içindeki **alıcının kim olduğunda** yatıyor.

```go
// alici.go — gomulu yontem cagrildiginda alici gomulu degerin kendisidir
package main

import "fmt"

type Taban struct {
	Ad string
}

func (t Taban) KimKonusuyor() string {
	return fmt.Sprintf("%T diyor ki: %s", t, t.Ad)
}

type Genisletilmis struct {
	Taban
	Sifre string
}

func main() {
	g := Genisletilmis{Taban: Taban{Ad: "Ayse"}, Sifre: "gizli"}
	fmt.Println("aliciyi soyleyen yontem:", g.KimKonusuyor())
}
```

```
aliciyi soyleyen yontem: main.Taban diyor ki: Ayse
```

`g.KimKonusuyor()` çağrısı `Genisletilmis` üzerinden yapıldı, ama yöntemin içinde `%T` ile
basılan tip `main.Taban` — `main.Genisletilmis` değil. Bu, alıcının `g`nin kendisi değil,
`g`nin içindeki gömülü `Taban` değeri olduğunu kanıtlıyor. Kalıtımı olan dillerde alt sınıf
üzerinden çağrılan bir üst sınıf yöntemi, kendini çağıran nesnenin **tam kimliğini**
görebilir; Go'da gömülü bir yöntem kendi tipinin dışına hiç çıkamıyor — `Taban`ın hiçbir
yöntemi `Genisletilmis`in var olduğunu bilmiyor, çünkü `Taban` tanımlandığı anda
`Genisletilmis` diye bir tip henüz yoktu. Gömme, derleyicinin `g.KimKonusuyor()` yazımını
`g.Taban.KimKonusuyor()`e **otomatik olarak açması**dır; yeni bir çağrı biçimi değil,
var olan bir çağrının kısaltılmış yazımı.

Bu, dış tip gömülü tiple **aynı adda** bir alan tanımladığında daha da belirginleşiyor.

```go
// golge.go — dis tipin ayni adli alani gomulu olani golgeler, ama yontem etkilenmez
package main

import "fmt"

type Taban struct {
	Ad string
}

func (t Taban) Selamla() string {
	return "Taban diyor: Merhaba " + t.Ad
}

type Genisletilmis struct {
	Taban
	Ad string // dis tipte ayni adli alan, gomulu olani golgeler
}

func main() {
	g := Genisletilmis{Taban: Taban{Ad: "GomuluAd"}, Ad: "DisAd"}
	fmt.Println("g.Ad (dogrudan)      :", g.Ad)
	fmt.Println("g.Taban.Ad (acikca)  :", g.Taban.Ad)
	fmt.Println("g.Selamla() (gomulu) :", g.Selamla())
}
```

```
g.Ad (dogrudan)      : DisAd
g.Taban.Ad (acikca)  : GomuluAd
g.Selamla() (gomulu) : Taban diyor: Merhaba GomuluAd
```

`Genisletilmis` kendi `Ad` alanını tanımladığında bu alan gömülü `Taban.Ad`ı **gölgeliyor**:
`g.Ad` artık dış tipin kendi alanına gidiyor (`"DisAd"`), gömülü alana ulaşmak için açıkça
`g.Taban.Ad` yazmak gerekiyor. Ama `g.Selamla()` çağrısı hâlâ `"GomuluAd"`ı kullanıyor —
çünkü `Selamla` `Taban`a ait ve `Taban`ın kendi `Ad`ı hiç gölgelenmedi, yalnızca dış tipten
**görünürlüğü** değişti. Bu, alıcının gömülü değerin kendisi olduğunun bir başka kanıtı:
dış tipin aynı adı yeniden kullanması, gömülü tipin kendi içindeki hiçbir şeyi değiştirmiyor.

## Özet

- Yazılmayan bir yapı alanı kendi tipinin sıfır değerini alır; bu, Sıfır Değerler
  dersindeki bağlayıcı dağılımın tanımlı sınıfına giren bir örnektir.
- Pozisyonel yapı değişmezleri alan sırasına bağlıdır; iki alan aynı tipteyse sıra
  değiştiğinde derleyici hiçbir şey yakalamaz, değerler sessizce yer değiştirir. Anahtarlı
  yazım bu kırılganlığı taşımaz.
- Bir yapının `==` ile karşılaştırılabilirliği kendi kuralı değildir; her alanının
  karşılaştırılabilirliğinin toplamıdır — bir dilim alan yapıyı karşılaştırılamaz yapar.
- Gömme, gömülü tipin alanlarını ve yöntemlerini dış tipe yazılmadan taşır; erişim doğrudan
  alan seçimiyle olur.
- Gömülü bir yöntem çağrıldığında alıcı gömülü değerin kendisidir, dış yapı değil; gömme
  kalıtım değildir, derleyicinin otomatik açtığı bir kısaltmadır.

## Sonraki Adım

Bu derste `Taban{Ad: "Ayse"}` yazımı bir değerdi ve `g.Ad`e erişim doğrudan o değer
üzerinden oldu. Ama bir yapıyı değiştirmek isteyen bir yöntem, kendi kopyası üzerinde
çalışırsa çağıranı hiç etkilemez — tıpkı dizinin fonksiyona geçtiğinde kopyalanması gibi.
Sıradaki ders bu soruna bakıyor: bir değişkenin **adresini** almadan bir yapıyı değiştirmek
neden mümkün değil, ve adres alındığında hangi çağrılar çağıranı etkiliyor?
