---
title: 'Go Temelleri'
source: 'https://academia.sh/tr/kurslar/go-temelleri'
language: tr
updated: '2026-08-23T14:24:59+00:00'
license: 'CC BY-SA 4.0'
lessons: 18
outcomes:
    - "Go'nun tip sistemi ve sıfır değer davranışıyla program yazar"
    - 'Dilim ve sözlük yapılarını maliyetleriyle kullanır'
    - 'Paket düzeni ve derleme komutlarını uygular'
    - 'Yazılmayan bir şeyin dilde mi derleyicide mi açıkta mı karşılandığını ölçerek ayırır'
---

# Go Temelleri

Go'da bir programı okurken en çok işe yarayan soru, yazılanın ne yaptığı değil yazılmayanın ne olduğudur: kurs her düzeneği tek bir soruyla ölçer — bunu yazmadığında dil tanımlı bir şey mi koyuyor, derleyici mi reddediyor, yoksa sonuç açıkta mı kalıyor? Sıfır değerler, dilimler, sözlükler, işaretçiler, paket düzeni ve komut ailesi aynı çerçeveden geçirilir.

## Dil Temelleri

Dilin tasarım amaçlarının okunabilirliğe ve derleme hızına bağlanması, modül başlatmayla ilk programın koşması, kısa bildirim ile sıralı sabitler, tam sayı ve kayan noktalı tiplerin ayrımı, ilk değeri verilmeyen her değişkenin tanımlı davranışı, koşul ve döngü biçimlerinin tek anahtar sözcükte toplanması, çoklu dönüş değeri ve değişken sayıda argümanın boş çağrıdaki karşılığı.

### Go'nun Tasarım Amaçları

Source: https://academia.sh/tr/kurslar/go-temelleri/gonun-tasarim-amaclari

Go derleyicisinin reddettiği dört şey — kullanılmayan değişken, kullanılmayan içe aktarım, yazılmayan dönüş değeri, örtük tip dönüşümü — program içinden çağrılan bir derleyiciyle koşturulur; dördü de derlemeyi durdurur ve örtük dönüşümün reddi aynı işi yapan kaynağı 15 karakterden 22 karaktere çıkarır.

M08'in altı kursu, aynı platforma altı ayrı soru sordu; sonuncusu bir davranışın kaynakta
hiçbir çağrının doğurmayabileceğini ölçtü — sınıf yolunda bulunan bir sağlayıcı, kaynakta
yazılı hiçbir satır olmadan devreye girebiliyordu. Bu kurs aynı kapıdan girer ama soruyu
platformdan **dilin kendisine** taşır: bir dil, yazmadığın şeyin karşılığını kendi belirler.
Kimi yerde bu karşılık tanımlı bir değerdir, kimi yerde dil yazılmamış olmayı hiç kabul etmez
ve kaynağı derlemeyi reddeder.

Bu ilk ders reddi ölçer. Go derleyicisi, çalışma zamanında hiçbir soruna yol açmayacak dört
biçimi bile kabul etmez: kullanılmayan bir değişken, kullanılmayan bir içe aktarım, yazılmayan
bir dönüş değeri, örtük bir tip dönüşümü. Dördü de başka dillerde çoğunlukla bir uyarıdır;
burada kaynak hiç sınıf dosyasına ya da yürütülebilir dosyaya dönüşmez. Sorulan soru şu:
bunlardan biri yazılmadığında derleyici ne yapıyor ve bu ret gerçekten bir kısıtlama mı,
yoksa yazılması gereken şeyi başka bir yere mi taşıyor?

Kursun geri kalanı boyunca aynı üçlü soru sorulacak: yazılmayan bir şey dilde **tanımlı** bir
karşılık mı buluyor, derleyici tarafından **reddediliyor** mu, yoksa sonuç çalışma zamanına
kalıp **açıkta** mı kalıyor? Bu ilk ders, üç yanıttan yalnız ortadakini — reddi — ele alıyor,
çünkü ret, dilin sınırlarını en keskin çizen yanıttır: dil bir şeye izin vermediğinde, o şeyin
nasıl davranacağını konuşmaya bile gerek kalmaz.

#### Zincir ve Bu Dersin Payı

Programlama Temelleri kursu (M01/K02) değişken tanımını, işleçleri, açık ve örtük dönüşümü
Python'la kurdu; orada örtük dönüşüm çoğunlukla sorunsuz geçen bir çalışma zamanı davranışıydı.
**Bu kavram burada tekrar kurulmaz** — farkı tek cümleyle yazmak yeter: Go'da aynı örtüklük bir
çalışma zamanı olayı değil, bir **derleme zamanı reddi**dir. Bilgisayarlar Nasıl Çalışır kursu
(M01/K01) derleyici–yorumlayıcı ayrımını kavram olarak kurdu; bu derste derleyici bir kavram
değil, **program içinden çağrılan bir araçtır** ve verdiği tek yanıt geçip geçmediğidir.

Veri Yapıları kursu (M01/K03) diziyi, listeyi ve sözlüğü soyut veri tipi olarak kurdu ve
karmaşıklıkla karşılaştırdı; bu müfredat o karşılaştırmayı tekrarlamaz. M09'un payı, kurulmuş
kavramların **Go'da hangi düzenekle gerçekleştiğidir** — bu ilk derste düzenek henüz bir veri
tipi değil, derleyicinin kaynağa karşı aldığı bir tavırdır. Sonraki dersler bu düzeneği
sırasıyla değişkenlere, tiplere ve denetim akışına taşıyacak.

#### Üç Tasarım Amacı, Tek Ölçülebilir Sonuç

Go'nun tasarım amaçları arasında üçü sık anılır: basitlik, derleme hızı, okunabilirlik. Bu
ders üçünü de doğrudan koşturmaz — süre bir ölçüm birimi değildir, çünkü bir derlemenin ne
kadar sürdüğü makineye ve o anki yüke bağlıdır ve bir koşumdan öbürüne değişir. Ama üçünün de
kaynakta **görünür bir iz** bıraktığı bir yer vardır: derleyicinin neyi reddettiği. Bir dil
her yapıyı kabul ederse, okuyanın hangi yolun kastedildiğini kaynaktan çıkarması gerekir; bir
dil bir yapıyı reddederse, o yapıyı yazan taraf niyetini kaynağa **taşımak** zorunda kalır.
Basitlik burada "az karakter yazmak" değil, dilin kabul ettiği biçim sayısının az olmasıdır:
bir işi yapmanın kaç yolu varsa, derleyicinin reddetmesi gereken yol sayısı da o kadar artar.
Derleme hızı, bu ret kararlarının **dosya sırasına bağlı olmamasından** gelir — bir dosyadaki
kullanılmayan içe aktarım, başka bir dosyanın nasıl derlendiğini beklemeden, o dosya tek
başına okunurken görülebilir. Okunabilirlik ise doğrudan bu dersin ölçtüğü şeydir: dört ret de,
kaynağı okuyan tarafın kaynaktaki her adı ve her tipi **tam olarak yazıldığı gibi** okuyabilmesi
için var.

Bu üç amaç birbiriyle çelişebilir görünür — az yazmak ile niyeti açıkça yazmak aynı yönde
durmaz. Bu dersin geri kalanı bu gerilimi ölçer: reddedilen dört biçim gerçekten daha kısa bir
yazımı mı engelliyor, yoksa örtük bırakılabilecek bir şeyi mi görünür kılıyor?

#### Derleyici Program İçinden Çağrılır

Ölçümün tamamı tek bir soruya iner: bir kaynak metni derlenir mi? Bu soruyu dış bir araca
sormadan, standart kitaplığın süreç başlatma ve dosya yazma yeteneğiyle yanıtlarız. Kaynak
metni geçici bir dizine bir modül dosyasıyla birlikte yazılır, derleyici o dizinde çalıştırılır
ve yalnız çıkış durumu okunur.

```go
// derleyici.go — derleyiciyi program icinden cagiran yardimci
package main

import (
	"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
}

func siniflaKaynak(kaynak string) string {
	if derlenirMi(kaynak) {
		return "derlendi"
	}
	return "derlenmedi"
}
```

**DT1.** Derleyici burada bir kavram değil bir çağrıdır: `derlenirMi`, kaynağı geçici bir
pakete yazıp derleyiciyi bir alt süreç olarak koşturur ve yalnız çıkış durumunu okur. Hata
metni hiç okunmaz, çünkü hata metninin biçimi sürüme bağlı veridir; kursun tamamında ölçülen
şey "neden reddedildi" değil, "reddedildi mi"dir.

Bu ayrımın bir bedeli var: `derlenirMi` yalnız `true` ya da `false` döndürdüğü için, bir kaynak
beklenenden **başka** bir nedenle de reddedilmiş olabilir ve ölçüm bunu ayırt edemez. DT2 bu
riski kapatıyor — dört kaynağın her biri elle kurulmuş ve tek bir yazılmamışlık dışında
derlenebilir olduğu doğrulanmıştır. Bu, derste gösterilen dört satırlık tabloyu okurken
akılda tutulması gereken bir sınırdır: `derlendi`/`derlenmedi` bir hata ayıklama aracı değil,
bir sınıflandırma aracıdır.

**DT2.** Aşağıdaki dört kaynağın her biri, tek bir yazılmamışlık dışında derlenebilir
durumdadır. Ret, o tek nedenden gelir — başka bir sözdizim hatasından değil. Bu derste ve
kursun geri kalanında ret ölçülürken bu koşul hep sağlanır.

Bu yöntemin bir dış araca ihtiyaç duymaması iki şeyi birden sağlıyor. Birincisi, ölçüm
**belirlenimcidir**: aynı kaynak metni her koşumda aynı yanıtı verir, çünkü derleyicinin kendisi
çağrılıyor, davranışı taklit eden ayrı bir yorumlayıcı değil. İkincisi, ölçüm **taşınabilirdir**:
`derlenirMi` yalnız standart kitaplığın süreç başlatma ve geçici dosya yazma yeteneğini
kullanıyor, kursun geri kalanında da aynı desen tekrarlanacak.

#### Dört Ret, Dört Neden

Aşağıdaki dört kaynak, aynı çerçevede yan yana koşturulur. Her biri, çalışma zamanında hiçbir
soruna yol açmayacak bir eksikliği taşıyor — hiçbiri sonsuz döngü, bellek sınırı aşımı ya da
tip uyuşmazlığı değildir. Buna rağmen dördü de derlenmez.

```go
// main.go — dort ret nedeni yan yana
package main

import "fmt"

func main() {
	fmt.Printf("%-32s %s\n", "neden", "sonuc")
	fmt.Printf("%-32s %s\n", "kullanilmayan degisken", siniflaKaynak(`package sinama
func F() { x := 1 }`))
	fmt.Printf("%-32s %s\n", "kullanilmayan ice aktarim", siniflaKaynak(`package sinama
import "fmt"
func F() {}`))
	fmt.Printf("%-32s %s\n", "donus degerinin yazilmamasi", siniflaKaynak(`package sinama
func F() int { }`))
	fmt.Printf("%-32s %s\n", "ortuk tip donusumu", siniflaKaynak(`package sinama
func F() { var a int32 = 1; var b int64 = a; _ = b }`))
}
```

```
neden                            sonuc
kullanilmayan degisken           derlenmedi
kullanilmayan ice aktarim        derlenmedi
donus degerinin yazilmamasi      derlenmedi
ortuk tip donusumu               derlenmedi
```

Dört satır da aynı sonucu veriyor, ama aynı gerekçeden gelmiyor. **Kullanılmayan değişken**
ile **kullanılmayan içe aktarım**, kaynakta yazılan bir şeyin hiç okunmamasını reddeder — dil,
"belki sonra kullanırım" diye bir ara durumu tanımaz; bir ad ya kullanılır ya da kaynakta hiç
yazılmaz. **Dönüş değerinin yazılmaması** farklı bir sınıftandır: imzada `int` sözü verilmiş
ama gövde hiçbir değer üretmiyor, derleyici sözü tutmayan gövdeyi kabul etmiyor. **Örtük tip
dönüşümü** ise üçüncü bir sınıf: `int32` bir `int64` değişkenine doğrudan atanamıyor, hedefteki
bit genişliği kaynaktakinden farklı olduğu için derleyici genişlemenin **yazılı** olmasını
istiyor.

Üçünün ortak noktası, hiçbirinin çalışma zamanında gözlemlenebilir bir hataya yol açmayacak
olmasıdır. Kullanılmayan bir değişken programı çökertmez, kullanılmayan bir içe aktarım hiçbir
şeyi bozmaz, `int32`den `int64`e genişleme hiçbir veri kaybetmez. Derleyici bunları çalışma
zamanı riskine göre değil, **kaynağın okunurluğuna** göre reddediyor: kullanılmayan bir ad
okuyanı yanıltır, örtük bir genişleme okuyana hangi tipin nerede durduğunu gizler. Ret burada
bir güvenlik önlemi değil, yazılmayanın karşılığını **daraltma** kararıdır — dil bu dört
durumda "tanımlı bir şey koymak" yerine "hiç izin vermemek"i seçiyor.

İki rettin (kullanılmayan değişken, kullanılmayan içe aktarım) ortak bir kaçış yolu var: dilin
**boş tanımlayıcısı**, `_`. Bir değeri `_`e atamak, "bu değeri biliyorum ama kullanmayacağım"
demenin yazılı bir yoludur; derleyici bunu kullanım sayar ve reddetmez. Bu, ilk bakışta ret
kararını delen bir kaçamak gibi görünür, ama aslında tam tersini yapar: `_` yazmak, kullanılmayan
bir adı **sessizce bırakmak** ile aradaki farkı ortadan kaldırır, çünkü ikisi de kaynakta aynı
ölçüde görünür olur — biri gerçek bir ad, öbürü kasıtlı bir "önemsemiyorum" işareti. Bu tanımlayıcı
kursun ilerleyen derslerinde tekrar karşımıza çıkacak: çoklu dönüş değerlerinden birini atlarken
ve bir sözlükte anahtarın varlığını sorarken de aynı sözcük kullanılıyor.

Her ret, bir düzeltme sırasında ortaya çıkan somut bir durumu yakalıyor. Kullanılmayan içe
aktarım, bir kod parçası taşınıp bir çağrı silindiğinde geride kalan bir satırdır; derleyici
onu tutmaya izin verseydi, kaynak zamanla artık kullanılmayan bağımlılıklarla dolardı ve hangi
içe aktarımın gerçekten gerekli olduğunu görmek güçleşirdi. Kullanılmayan değişken de benzer
bir iz taşır: bir hesaplamanın ara sonucu bir adla tutulup sonra kullanılmadan bırakıldığında,
bu çoğunlukla yarım kalmış bir düzenlemenin işaretidir. Yazılmayan dönüş değeri farklı bir
kaynaktan geliyor — imza bir söz, gövde o sözü tutmalı; derleyici bunu, çağıran tarafın imzaya
bakarak kurduğu varsayımı korumak için zorluyor. Örtük tip dönüşümü ise en dolaylı olanı: iki
tam sayı tipi aynı ailede olsa bile bit genişlikleri farklıysa, bir değeri diğerine sessizce
kopyalamak, kaynağı okuyan tarafın gözünden **hiç olmamış** bir genişleme demektir; derleyici
bu genişlemeyi görünür kılmayı, sessizce geçirmeye tercih ediyor.

#### Sınırlayıcı Ölçüm: Reddetmek Daha Az Yazmak Demek Değildir

Ret kararının bedelsiz olduğunu düşünmek yanıltıcıdır. Örtük dönüşümün reddini ele alalım:
`var b int64 = a` satırı, `a` bir `int32` iken derlenmiyor. Aynı işi yapan, derlenen satır
`var b int64 = int64(a)`dır — dönüşüm çağrısı **yazılmak zorunda**. İki satırı birebir
karşılaştıralım.

```go
// sinirlayici.go — reddetmek daha az yazmak demek degil mi
package main

import "fmt"

func main() {
	ortukSatir := "var b int64 = a"
	acikSatir := "var b int64 = int64(a)"

	ortukKaynak := "package sinama\nfunc F() { var a int32 = 1; " + ortukSatir + "; _ = b }"
	acikKaynak := "package sinama\nfunc F() { var a int32 = 1; " + acikSatir + "; _ = b }"

	fmt.Printf("%-38s %-11s uzunluk=%d\n", "ortuk donusum", siniflaKaynak(ortukKaynak), len(ortukSatir))
	fmt.Printf("%-38s %-11s uzunluk=%d\n", "acik donusum", siniflaKaynak(acikKaynak), len(acikSatir))
}
```

```
ortuk donusum                          derlenmedi  uzunluk=15
acik donusum                           derlendi    uzunluk=22
```

Sonuç ilk bakışta tersine dönmüş görünüyor: derlenmeyen satır **kısa** (15 karakter), derlenen
satır **uzun** (22 karakter). Reddetmek, kaynağı kısaltmıyor — tam tersini yapıyor. Derleyici,
`a`nın genişletileceğini varsaymak yerine bu genişletmenin kaynakta **görünür** olmasını
istiyor ve bunun bedelini yazan tarafa yüklüyor: yedi karakterlik bir çağrı.

**DT3.** Burada ölçülen birim karakterdir, çalışma süresi ya da bellek değildir; iddia
"reddetmek yavaşlatır" değil, "reddetmek kısaltmaz"dır. On beş karakterlik fark küçük görünse
de, aynı kalıp bir kaynağın her genişleme noktasında tekrarlanır — okunabilirlik kazancı, satır
başına sabit bir yazım bedeliyle satın alınıyor.

Dördü aynı yönde değil. Kullanılmayan içe aktarımın düzeltmesi bir **silmedir** — satır kaynaktan
tamamen çıkar ve kaynak kısalır. Örtük tip dönüşümünün düzeltmesi ise bir **eklemedir** — var
olan bir satıra bir çağrı sarılır ve kaynak uzar. İki ret de aynı sınıftan (derlenmedi) sonuç
verir, ama düzeltmenin yönü tersinedir. Bu, "reddetmek" tek bir yazım maliyeti taşıdığı
izlenimini kırıyor: bazı ret nedenleri kaynağı sadeleştirmeye, bazıları niyeti açıkça yazmaya
zorluyor; ortak olan tek şey, ikisinin de kaynakta **belirsiz bir aralık** bırakmamasıdır.

Bu, dördüncü rette (kullanılmayan değişken) de aynı biçimde görülür: `x := 1` satırını silmek
kaynağı kısaltır, ama `x`i bir yere kullanmak (`_ = x` yazmak) da kısaltmaz, yalnızca bir
sözcük ekler. Reddin karşılığı hiçbir zaman "daha az yaz" değildir; ya sil ya da niyetini
**açıkça yaz**. Go'nun tasarım amaçları arasında sayılan basitlik, kod uzunluğunu değil,
kaynakta neyin göründüğünü hedefliyor: bir okuyucu, `int64(a)` satırını gördüğünde genişlemenin
kasıtlı olduğunu bilir; `var b int64 = a` satırını görseydi bunu bilemezdi.

Bu okuma, önceki bölümde açılan gerilimi kapatıyor. Basitlik ile niyeti açıkça yazmak, kaynağın
**uzunluğu** düzeyinde çelişir ama kaynağın **kural sayısı** düzeyinde çelişmez: derleyicinin
tanıdığı dönüşüm kuralı tektir — genişleme her zaman yazılır — ve bu tek kural, "hangi
durumda örtük dönüşüm güvenlidir" sorusunu okuyandan tamamen kaldırıyor. Okuyan taraf, bir
atamanın örtük mü açık mı olduğunu ayrıca sorgulamak zorunda kalmıyor, çünkü örtük olanı
derleyici zaten geçirmiyor. Basitlik burada azalan karakter sayısında değil, azalan **olası
yorum sayısında** duruyor.

Derleme hızıyla bağ da aynı noktadan kuruluyor. Dört ret de, kararını verirken kaynağın
geri kalanını beklemez: kullanılmayan bir içe aktarım, o dosya tek başına okunurken bellidir;
örtük bir dönüşüm, atamanın iki yanındaki tipe bakılarak yerinde görülür. Hiçbiri, programın
başka bir yerinde ne olduğuna bağlı bir çıkarım gerektirmiyor. Bu derste süre hiç ölçülmedi,
ama ret kararlarının **yerelliği** — her birinin yalnız kendi bulunduğu ifadeye bakması —
derleme hızının nereden geldiğini gösteren gözlemlenebilir bir iz bırakıyor.

#### Özet

- Derleyici bu derste ve kursun geri kalanında program içinden çağrılır; yalnız geçip
  geçmediği okunur, hata metni hiç okunmaz.
- Kullanılmayan değişken, kullanılmayan içe aktarım, yazılmayan dönüş değeri ve örtük tip
  dönüşümü — dördü de çalışma zamanında zararsız olduğu hâlde derlemeyi durduruyor.
- Üç neden üç ayrı sınıftandır: yazılıp okunmayan bir ad, sözü tutmayan bir gövde, kaynakta
  görünmeyen bir genişleme.
- Örtük dönüşümün reddi kaynağı kısaltmaz: derlenmeyen satır 15 karakter, derlenen karşılığı
  22 karakterdir.
- Ret, yazılmayanın karşılığını daraltma kararıdır — dil bu dört durumda tanımlı bir değer
  koymak yerine hiç izin vermemeyi seçer.

#### Sonraki Adım

Dört ret nedeni, bir kaynağın **fazladan** yazdığı ya da eksik bıraktığı şeylerle ilgiliydi;
hiçbiri kaynağın **çalışabilmesi** için gereken en küçük kümeyle ilgili değildi. Sıradaki ders
bu küçük kümeyi sorar: bir kaynağın derlenip çalışabilmesi için hangi parçalar zorunludur ve
bu parçalardan biri kaynağın kendisinde değil de modül dosyasında yazılıyorsa, onu kaldırmak
neye mal olur?

### Ortam Kurulumu ve İlk Program

Source: https://academia.sh/tr/kurslar/go-temelleri/ortam-kurulumu-ve-ilk-program

Bir Go kaynağının derlenebilmesi için üç şey zorunludur: paket bildirimi, giriş noktası (func main) ve modül dosyası; üçünden biri eksik olduğunda, kaynağın geri kalanı hiç değişmese bile derleme reddediliyor — modül dosyası kaynağın kendisinde hiç durmuyor.

Önceki ders dört ret nedenini ölçtü; hepsi kaynağa **fazladan** bir şey yazıldığında ya da bir
sözün tutulmadığında ortaya çıkıyordu. Bu ders tersini soruyor: bir kaynağın hiç derlenip
çalışabilmesi için yazılması **zorunlu** olan en küçük küme nedir? Bir Go programı üç şeyi
birden ister — bir paket bildirimi, bir giriş noktası, bir modül dosyası — ve bunlardan biri
eksik olduğunda kaynağın geri kalanı ne kadar doğru yazılmış olursa olsun derleme durur.

Bilgisayarlar Nasıl Çalışır kursu (M01/K01) bir programın işletim sistemi tarafından nasıl
başlatıldığını, giriş noktası kavramını ve bir sürecin ilk komuta nasıl ulaştığını kavram
olarak kurdu. **Bu burada tekrar kurulmaz** — farkı tek cümleyle yazmak yeter: orada giriş
noktasının **var olması gerektiği** anlatıldı, burada Go'nun bu gerekliliği **hangi imzayla**
ve **derleme zamanında** denetlediği ölçülüyor.

#### Üç Zorunlu Parça

Üçü de farklı bir yerde durur. Paket bildirimi (`package main`) kaynağın **ilk satırıdır** ve
hangi paketin parçası olduğunu söyler; çalıştırılabilir bir program için bu paket adı `main`
olmak zorundadır. Giriş noktası, `main` paketi içinde tanımlanan `func main()` işlevidir ve
sanal makinenin değil, doğrudan işletim sisteminin çağıracağı ilk Go koludur. Modül dosyası ise
kaynağın **dışındadır** — kaynakla aynı dizinde duran ayrı bir metin dosyası, `go.mod` ve
derleyiciye "bu dizin bir modülün köküdür" diyen tek şey odur.

Üçünün de zorunlu olması bir tesadüf değil, üç ayrı sorunun yanıtı. Paket bildirimi "bu kaynak
neyin parçası" sorusuna yanıt verir ve tek bir kaynak dosyasının bile paketsiz duramayacağını
söyler — Go'da paketsiz bir kaynak diye bir şey yoktur, en küçük program bile bir paket adı
taşımak zorundadır. Giriş noktası "çalıştırma buradan mı başlıyor" sorusuna yanıt verir; bir
paket, birden fazla işlev barındırabilir ama işletim sistemine hangi işlevden başlanacağını
söyleyen tek bir imza vardır. Modül dosyası ise "bu paket nerede duruyor" sorusuna yanıt verir
ve paketin kendisinden bağımsız bir bilgi taşır — aynı paket adı birçok farklı dizinde tekrar
edebilir, ama bir dizinin hangi modüle ait olduğu yalnız o dizindeki (ya da üstündeki) `go.mod`
dosyasından okunur.

Üç sorunun üçü de "zorunlu" göründüğü için, bunları ayrı ayrı doğrulamak yerine tek bir kontrol
listesi gibi ele almak cazip gelebilir — "üçü de eksiksiz mi" diye tek bir denetim. Bu ders
tam tersini yapıyor: her birini **teker teker** kaldırıp sonucu ayrı ayrı okuyor, çünkü aynı
`derlenmedi` yanıtının arkasında üç ayrı neden durabiliyor ve bu üç nedenin hangi aşamada
ortaya çıktığını bilmek, bir kaynağın neden derlenmediğini anlamanın ilk adımı.

Bu üç parçayı tek tek kaldırıp derlenme sonucunu ölçeceğiz. Ölçüm aracı, önceki dersin
`derlenirMi` işlevine çok yakın; tek farkı, modül dosyasının yazılıp yazılmayacağını ayrıca bir
parametre olarak almasıdır.

```go
// derleme.go — parcalari denetleyerek derleyen yardimci
package main

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

func derlenirMiParcalar(kaynak string, modYaz bool) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	if modYaz {
		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 sinif(ok bool) string {
	if ok {
		return "derlendi"
	}
	return "derlenmedi"
}
```

**DT4.** Ölçüm aracı bir önceki dersten tek bir parametre uzağa açılıyor: `modYaz`, modül
dosyasının o koşumda yazılıp yazılmayacağını belirler. Kaynağın kendisi bu parametreden hiç
etkilenmez; bu, aşağıdaki dördüncü satırın neyi ölçtüğünü doğru okumak için gerekli.

Bu genişleme küçük görünse de bir tasarım kararı taşıyor: önceki derste yazılmamışlığın yeri
her zaman kaynağın **içindeydi** — bir satır ya vardı ya yoktu. Bu derste yazılmamışlığın yeri
ilk kez kaynağın **dışına** taşınıyor ve ölçüm aracının bunu ayrı bir bağımsız değişkenle
temsil etmesi gerekiyor. Bir sonraki bölümde bu ayrımın neden önemli olduğu görülecek.

Dört durumu sırayla koşturuyoruz: hiçbir parça eksik değilken, paket bildirimi yokken, giriş
noktası yokken, modül dosyası yokken.

```go
// parcalar.go — uc zorunlu parcanin her biri tek tek kaldirilir
package main

import "fmt"

const tamKaynak = `package main

func main() {}
`
const paketsizKaynak = `func main() {}
`
const anasizKaynak = `package main
`

func main() {
	fmt.Printf("%-30s %s\n", "eksik parca", "sonuc")
	fmt.Printf("%-30s %s\n", "hicbiri eksik degil", sinif(derlenirMiParcalar(tamKaynak, true)))
	fmt.Printf("%-30s %s\n", "paket bildirimi yok", sinif(derlenirMiParcalar(paketsizKaynak, true)))
	fmt.Printf("%-30s %s\n", "giris noktasi (main) yok", sinif(derlenirMiParcalar(anasizKaynak, true)))
	fmt.Printf("%-30s %s\n", "modul dosyasi yok", sinif(derlenirMiParcalar(tamKaynak, false)))
}
```

```
eksik parca                    sonuc
hicbiri eksik degil            derlendi
paket bildirimi yok            derlenmedi
giris noktasi (main) yok       derlenmedi
modul dosyasi yok              derlenmedi
```

İlk satır kontrol grubudur: hiçbir parça eksik değilken kaynak derlenir. Sonraki üç satırın her
biri, kontrol grubundan tek bir parçayı çıkarır ve sonuç aynı yönde değişir — üçü de derlemeyi
durdurur. Ama üçü aynı **nedenden** durdurmaz. Paket bildirimi yokken derleyici kaynağın hangi
pakete ait olduğunu hiç bilmiyor; bu, en erken kesilen daldır, çünkü paket adı kaynağın ilk
satırında beklenir. Giriş noktası yokken kaynak geçerli bir paket olarak **derlenir** ama
yürütülebilir bir dosya üretilmez — kaynakta hata yoktur, yalnız çalıştırılabilir bir uçtan
yoksundur; `go build ./...` bunu da derlenmedi sayıyor, çünkü `main` paketi için giriş noktası
zorunlu bir sözleşmedir. Dördüncü satır ise en dolaylı olanı ve dersin sınırlayıcı ölçümüne
taşıyor.

`paketsizKaynak` sabitine bakılırsa, eksik olan tek şey bir satırdır: `package main`. Bu satır
kaldırıldığında geriye kalan `func main() {}` başlı başına anlamsız değildir — bir işlev
tanımıdır ve sözdizimi doğrudur. Derleyicinin reddi burada sözdizimine değil, **bağlama**
yöneliktir: her Go dosyası bir paketin parçası olarak okunur ve paket adı olmadan bir dosyayı
hiçbir pakete yerleştiremez. Bu, önceki dersteki dört retten farklı bir tondadır: oradaki
dört kaynak sözdizimi kurallarını çiğniyordu, buradaki kaynak sözdizimi açısından eksiksiz bir
işlev gövdesidir ama **kimliksizdir**.

İlk iki ret, önceki dersin ölçtüğü dört rete yakın bir aileden: ikisi de kaynağın kendi
sözdiziminde eksik bırakılan bir şeydir ve derleyici bunu kaynağı **okurken** görür. Giriş
noktasının yokluğu farklı bir aşamada yakalanıyor. `go build` iki işi art arda yapar: önce
kaynağı bir pakete derler, sonra o paketten çalıştırılabilir bir dosya **bağlar**. Paket
derlemesi `main.go`yu geçerli bir Go dosyası olarak kabul eder; bağlama aşaması, `main`
paketinin bir `func main()` işlevi taşımasını arar ve bulamayınca durur. Bu ayrım önemlidir,
çünkü aynı kaynak farklı bir bağlamda —örneğin başka bir programın içe aktardığı bir kitaplık
paketi olarak— hiçbir sorun çıkarmadan derlenebilirdi; giriş noktası zorunluluğu yalnız `main`
paketine, yalnız çalıştırılabilir bir dosya istendiğinde uygulanıyor.

#### İki Dosya, Tek Paket

Üç zorunlu parça, tek bir dosyada mı durmak zorunda? Değil. Aynı dizindeki iki dosya, aynı
paket bildirimini taşıdığı sürece **tek pakete** ait sayılır ve birlikte derlenir — biri
`func main` taşımasa bile. Yukarıdaki `derleme.go` ile bu derste kullanılan diğer bütün
dosyalar zaten bunu gösteriyordu; burada aynı deseni en yalın hâliyle kuruyoruz.

Bunun pratik önemi, bir programın büyümesiyle ortaya çıkıyor. `func main()` içeren tek bir
dosyaya her şeyi yazmak zorunda olsaydık, bir programın uzunluğu tek dosyanın okunabilirliğiyle
sınırlı kalırdı. Go, bir paketi dosya sayısına göre değil, paket bildirimine göre tanımladığı
için, aynı paketin sorumluluklarını birden fazla dosyaya dağıtmak mümkün — üstelik bu bölünme
derleme sonucunu hiç değiştirmiyor: `derleme.go`, `yardim.go` ve `birlikte.go` üç ayrı dosya
olarak da tek bir dosyaya birleştirilmiş gibi de derlenirdi, aynı ikili dosyayı üretirdi.
Bölünme kararı, tamamen okuyan ve yazan tarafın rahatlığı için var.

```go
// yardim.go — ikinci dosya, ayni pakete ait
package main

import "fmt"

func selamla(ad string) string {
	return fmt.Sprintf("merhaba, %s", ad)
}
```

```go
// birlikte.go — iki ayri dosya, tek paket, birlikte derlenir
package main

import "fmt"

func main() {
	fmt.Println(selamla("paket"))
}
```

```
merhaba, paket
```

**DT5.** `birlikte.go`, `yardim.go`'da tanımlanan `selamla` işlevini hiçbir içe aktarım
yazmadan çağırıyor. Bu derste ölçülen şey ikinci bir dosyanın varlığı değil — onu bu derse
kadar zaten kullanıyorduk — iki ayrı dosyanın **tek bir ikili dosyaya** derlendiğidir: `go run
.` dizindeki bütün `.go` dosyalarını okur, aralarında içe aktarım aramaz, yalnız paket adının
eşleştiğini denetler.

Bu davranış, önceki ölçümdeki "hiçbiri eksik değil" satırını da baştan okumamızı gerektiriyor.
O satırda kullanılan tek dosyalık `tamKaynak`, `derleme.go`nun **dışında**, kendi başına bir
pakete yazılıp derlenmişti — orada `sinif` ve `derlenirMiParcalar` çağrılmıyordu, kaynak yalnız
`package main` ve boş bir `func main` taşıyordu. Burada ise `birlikte.go`, aynı dizindeki
`yardim.go`ya bağımlı ve o bağımlılık dosya sınırını hiç aşan bir içe aktarım gerektirmiyor.
İki ölçüm birbirini tamamlıyor: biri paketin en küçük hâlini, öbürü paketin birden fazla
dosyaya yayılmış hâlini gösteriyor.

Bu, paket bildiriminin neden dosya başına yazıldığını da açıklıyor: derleyici, hangi
dosyaların birlikte bir bütün oluşturduğunu **dosya adından değil**, her dosyanın kendi ilk
satırından öğreniyor. `derleme.go`, `yardim.go` ve `birlikte.go` üçü de `main` yazıyor ve
üçü de aynı pakete katılıyor; herhangi biri farklı bir ad yazsaydı derleme aynı dizinde bile
dururdu.

Bu davranışın bir sonucu, içe aktarımların **iki farklı düzeyde** çalıştığıdır. `birlikte.go`,
`fmt` paketini kendi başına içe aktarıyor, çünkü standart kitaplığın bir parçasını kullanıyor;
ama `yardim.go`da tanımlanan `selamla`yı içe aktarmıyor, çünkü o zaten aynı paketin bir
parçası. İçe aktarım satırı yalnız **paket sınırını** geçen adlar için gerekiyor — aynı paketin
içinde, dosya sınırı bir anlam taşımıyor. Kursun paketler konusu bu sınırı tersine çevirecek:
orada dosyalar aynı pakette kalmaya devam edecek, ama bazı adlar paketin **dışından** hiç
görünmeyecek.

#### Sınırlayıcı Ölçüm: Modül Dosyası Kaynağın İçinde Değildir

Dördüncü satıra dönelim. `modul dosyasi yok` satırında kullanılan kaynak, ilk satırdakiyle
**bayt bayt aynıdır** — `tamKaynak` sabiti iki çağrıda da değişmeden geçiyor. Aradaki tek fark,
`derlenirMiParcalar`'a verilen `modYaz` bağımsız değişkeninin `false` olmasıdır: geçici dizine
`go.mod` hiç yazılmıyor.

**DT6.** Bu satırda ölçülen şey bir kaynak hatası değil, bir **bağlam eksikliğidir**. Kaynağın
kendisinde paket bildirimi de giriş noktası da eksiksiz duruyor; derleyici yine de reddediyor,
çünkü hangi modülün parçası olduğunu bilmeden bir dizini derlemeyi kabul etmiyor.

Bu satır, dersin ilk iki reddinden kalitatif olarak ayrılıyor. Paket bildirimini ya da giriş
noktasını kaldırırken kaynağa **dokunuyorduk**; burada kaynak hiç açılmadı bile, yalnız
dizindeki başka bir dosya silindi. Bir okuyucu, `tamKaynak` sabitine baksa, onun neden bazen
derlendiğini bazen derlenmediğini kaynağın kendisinden çıkaramaz — cevap kaynağın dışında,
dizinin içeriğinde duruyor.

Bunun pratik sonucu şu: bir kaynak dosyasını başka bir dizine kopyalamak, o dizinde bir modül
dosyası yoksa hiçbir işe yaramaz — kaynak aynen kopyalanmış olsa bile derlenmez. Modül dosyası,
paket bildirimi gibi kaynağın **parçası** değildir; kaynağın **bağlamıdır**. Bu ayrım önemlidir
çünkü ikisi de "zorunlu" sayıldığı hâlde farklı bir şeye zorunludur: paket bildirimi kaynağın
kendi kimliğini taşır, modül dosyası kaynağın **nerede** durduğunu bildirir. Kursun paketler
konusu bu ayrımı derinleştirecek; burada ölçülen yalnız zorunluluğun kendisidir — modül dosyası
olmadan hiçbir kaynak, ne kadar doğru yazılırsa yazılsın, bir ikili dosyaya dönüşmez.

Bu derste kullanılan ölçüm aracı, modül dosyasının **içeriğini** hiç değiştirmedi — yalnız var
olup olmadığını değiştirdi. Bir modül dosyası, kendi başına, dışarıdan hiçbir bağımlılık
istemeyen en küçük hâliyle bile zorunludur; içine hiçbir bağımlılık satırı eklenmemiş olması,
derleyicinin modülü reddetmesi için bir neden değildir. Zorunlu olan modül dosyasının **var
olması**, içeriğinin ne kadar dolu olduğu değil.

Modül dosyasının kaynaktan bağımsız olması, aynı kaynağın **birden fazla** modülün altına
taşınabileceği anlamına da geliyor: dizini değiştirip yeni bir `go.mod` yazmak, kaynağı hiç
değiştirmeden onu başka bir modülün parçası yapar. Paket bildirimini değiştirmek ise kaynağın
kendisini değiştirmek demektir. İkisi arasındaki bu fark, "zorunlu" sözcüğünün tek bir anlama
gelmediğini gösteriyor — bir şey kaynağın **içinde** zorunlu olabilir, bir şey kaynağın
**etrafında** zorunlu olabilir ve ikisinin kaldırılma bedeli aynı değildir: biri kaynağı
bozar, öbürü yalnız kaynağın bulunduğu yeri geçersiz kılar.

Bu üçlü ayrım — kimlik, sözleşme, bağlam — ilk derste ölçülen dört rede de geriye dönük bir
çerçeve veriyor. Kullanılmayan değişken ve kullanılmayan içe aktarım, kaynağın kendi kimliğiyle
ilgiliydi: kaynakta yazılan her ad, kaynağın bir parçası olarak kalmayı hak etmeli. Yazılmayan
dönüş değeri bir sözleşme ihlaliydi: imza bir söz verir, gövde onu tutmalı. Bu derste eklenen
üçüncü kategori — bağlam — ilk derste hiç görünmedi, çünkü o dersteki dört kaynak zaten bir
modülün içine yazılıyordu. Zorunluluğun üç ayrı yerde durabileceğini görmek, kursun ilerleyen
derslerinde tekrar işe yarayacak: bir değişkenin sıfır değeri onun **kimliğine**, bir işlevin
adlandırılmış dönüşü onun **sözleşmesine**, bir paketin dışa aktarma kuralı ise onun
**bağlamına** ait olacak. Bu üç kategori bu kursta hiçbir yerde adıyla anılmayacak — burada
kurulan yalnız bir okuma alışkanlığı: bir `derlenmedi` yanıtı görüldüğünde ilk soru "hangi
satır eksik" değil, "eksik olan şey kaynağın neresinde duruyor" olmalı.

#### Özet

- Bir Go kaynağının derlenebilmesi için üç şey zorunludur: paket bildirimi, giriş noktası
  (`func main`), modül dosyası.
- Üçü de eksikken derleme durur, ama üçü aynı nedenden durmaz: biri kaynağın kimliğini, biri
  sözleşmesini, biri bağlamını eksik bırakır.
- Aynı dizindeki iki dosya, aynı paket adını taşıdığı sürece içe aktarım yazmadan birbirini
  görür ve tek bir ikili dosyaya derlenir.
- Modül dosyası kaynağın parçası değil bağlamıdır: aynı kaynak, hiç değişmeden, modül dosyası
  kaldırıldığında derlenmez hâle gelir.
- Zorunluluk tek bir anlama gelmiyor — kaynağın içinde zorunlu olanla kaynağın etrafında
  zorunlu olan, kaldırıldığında farklı bir bedel ödetiyor.

#### Sonraki Adım

Bu derste bir kaynağın **çalışabilmesi** için gerekenler ölçüldü; hiçbir yerde bir değere ne
yazılıp ne yazılmadığı sorulmadı. `func main() {}` boş bir gövdeydi ve içinde hiçbir değişken
yoktu. Sıradaki ders bu boşluğu dolduruyor: bir değişkeni tanımlarken tipini yazmak zorunda
mısın, kısa bildirim tipi nereden çıkarıyor ve sıralı bir sabit dizisinde yazılmayan değerleri
kim dolduruyor?

### Değişkenler ve Sabitler

Source: https://academia.sh/tr/kurslar/go-temelleri/degiskenler-ve-sabitler

Kısa bildirim bir değişkenin tipini kaynakta hiç yazmadan belirliyor, altı sabitlik bir iota dizisinin yalnız ikisinde değer kaynakta yazılı — dördü bir önceki ifadenin tekrarından geliyor — ve aynı kısa bildirim satırı paket düzeyinde reddediliyor, fonksiyon içinde kabul ediliyor.

Önceki ders bir kaynağın çalışabilmesi için hangi parçaların zorunlu olduğunu ölçtü, ama
`func main() {}` boyunca gövde hep boştu — hiçbir değere ne yazılıp ne yazılmadığı hiç
sorulmadı. Bu ders o boşluğa giriyor. Bir değişken tanımlarken tipini yazmak zorunda mısın,
kısa bildirim yazmadığın tipi nereden çıkarıyor ve sıralı bir sabit dizisinde aradaki
değerleri kim dolduruyor?

Programlama Temelleri kursu (M01/K02) değişken tanımını ve bağlamayı Python'la kurdu; orada
bir adın bir değere bağlanması tek bir yazımla oluyordu ve tip kaynakta hiç yer almıyordu.
**Bu kavram burada tekrar kurulmaz** — farkı tek cümleyle yazmak yeter: Go'da bir adın **bir
tipi vardır** ve bu tip ya yazılır ya da bir değerden çıkarılır; hiçbir değişken tipsiz
kalmaz. Bu dersin sorduğu soru, o çıkarımın nereden geldiğidir.

#### Kısa Bildirim Tipi Nereden Çıkarıyor

Go'da bir değişken iki biçimde tanımlanır: `var ad tip = değer` ya da kısa bildirim, `ad :=
değer`. İkincisinde tip hiç yazılmaz; derleyici, atanan değerin **kendi** tipine bakarak
değişkenin tipini belirler. Bu, "yazılmayan bir şeyin tanımlı bir karşılığı var" örneklerinden
biridir — tip yazılmadı, ama değişkenin bir tipi olmaması diye bir seçenek yok, derleyici
o tipi değerden **çıkarıyor**.

Çıkarımın hangi tipe vardığını, çalışma zamanında `%T` biçim doğrulayıcısıyla sorabiliriz; bu
doğrulayıcı bir değerin **statik** tipinin adını yazdırır ve bu ad derleme zamanında sabitlenir
— her koşumda aynı yanıtı verir.

```go
// tur.go — kisa bildirimin cikardigi tip
package main

import "fmt"

func main() {
	tamsayi := 42
	kayanNokta := 3.14
	dizgi := "metin"
	mantiksal := true

	fmt.Printf("%-12s %-8s deger=%v\n", "tamsayi", fmt.Sprintf("%T", tamsayi), tamsayi)
	fmt.Printf("%-12s %-8s deger=%v\n", "kayanNokta", fmt.Sprintf("%T", kayanNokta), kayanNokta)
	fmt.Printf("%-12s %-8s deger=%v\n", "dizgi", fmt.Sprintf("%T", dizgi), dizgi)
	fmt.Printf("%-12s %-8s deger=%v\n", "mantiksal", fmt.Sprintf("%T", mantiksal), mantiksal)

	var genisTip float64 = 42
	fmt.Printf("%-12s %-8s deger=%v\n", "genisTip", fmt.Sprintf("%T", genisTip), genisTip)
}
```

```
tamsayi      int      deger=42
kayanNokta   float64  deger=3.14
dizgi        string   deger=metin
mantiksal    bool     deger=true
genisTip     float64  deger=42
```

**DT7.** `%T`, kaynağın kendisinden okunan statik bir addır; makineye, sürüme ya da koşuma
bağlı hiçbir bilgi taşımaz. Bu derste ve kursun geri kalanında tip adı okumak için hep bu
doğrulayıcı kullanılacak, çünkü ortama bağlı veri yasağını hiç ihlal etmiyor.

Bu, ölçüm için önemli bir ayrımı gösteriyor: `%T`nin yazdırdığı ad, bir bellek adresi ya da bir
değişkenin kendisi değil, derleyicinin o değişkene atadığı **tip bilgisinin adıdır**. İki farklı
koşumda aynı kaynak çalıştırıldığında `%T` her zaman aynı dizgiyi üretir, tıpkı bir önceki
derste `derlenirMi`nin her zaman aynı `derlendi`/`derlenmedi` yanıtını üretmesi gibi. Kursun
ölçüm araçlarının hepsi bu iki özelliği paylaşıyor: belirlenimci olmak ve ortama bağlı hiçbir
şey okumamak.

İlk dört satır, kısa bildirimin dört ayrı değerden dört ayrı tipe vardığını gösteriyor: tam
sayı yazınca `int`, ondalıklı yazınca `float64`, tırnak içinde yazınca `string`, `true`/`false`
yazınca `bool`. Hiçbirinde tip adı kaynakta geçmiyor; kısa bildirimin solundaki `:=` işareti,
"bu adın tipini sağdaki değerden çıkar" demenin kısaltmasıdır.

Bu çıkarım, atanan değerin **yazım biçimine** bakarak çalışıyor, çalışma zamanındaki bir
sınamaya değil. `42` yazıldığında derleyici bunun bir tam sayı sabiti olduğunu kaynağı okurken
görüyor; hiçbir değer üretilmeden, program hiç çalıştırılmadan bu karar veriliyor. Bu, kısa
bildirimin de bir derleme zamanı kararı olduğunu gösteriyor — yalnız tipin adı kaynakta
yazılmıyor, kararın kendisi hâlâ derleme sırasında, çalışma zamanından önce alınıyor. Kısa
bildirimin çalışma zamanına bıraktığı hiçbir şey yok; yazılmayan yalnızca **görünür harf
dizisidir**, karar değildir.

#### `var` ile Yazılan Tip Nasıl Ayrılıyor

Beşinci satır farklı bir yol izliyor. `42` sayısı kısa bildirimle yazılsaydı `int` olurdu —
ilk satır bunu zaten gösterdi. Burada aynı `42` değeri, `var genisTip float64 = 42` satırıyla
**açıkça** `float64` yapılıyor. Kısa bildirim tipi değerden çıkarırken, `var` tipi elle
belirlemenin yolunu açıyor; ikisi arasındaki fark, kimin karar verdiğidir. Kısa bildirimde
karar değere aittir, `var`da karar yazan tarafa aittir.

Bu, önceki derste ölçülen örtük dönüşüm reddiyle karıştırılmamalı. `var genisTip float64 = 42`
bir dönüşüm değil, bir **ilk değer atamasıdır** — `42` burada tipsiz bir sabittir ve tipsiz
sabitler hedef tipe göre biçimlenir. Bir dönüşüm, zaten tipi belli olan bir **değişkeni** başka
bir değişkene atamaya çalıştığında devreye giriyordu (`var a int32 = 1; var b int64 = a`).
Aradaki fark ilerleyen derste tekrar ölçülecek; burada not edilmesi gereken, kısa bildirimin
sonucunu `var` ile bastırmanın bir ret değil, bir **tercih** olduğudur.

`var` yazımının bir üstünlüğü daha var: bir ilk değer olmadan da kullanılabiliyor.
`var sayac int` yazmak, sayacı hiçbir değer vermeden tanımlıyor ve derleyici bu satırı hiç
reddetmiyor — kısa bildirimin tersine, `var` bir değer istemez. Bu durumda değişkenin aldığı
değer, bu kursun ilerleyen bir dersinin konusu olan **sıfır değer**dir; burada yalnız şu not
edilsin: `var`ın tip yazmaya izin vermesi ile değer yazmadan da çalışabilmesi aynı kökten
geliyor — ikisi de, kısa bildirimin aksine, "bir değerden çıkarım" zorunluluğuna bağlı değil.

#### Iota: Yazılmayan Sabit Değerleri Kim Dolduruyor

Sabitler, `const` ile tanımlanır ve bir grup içinde art arda sıralandığında `iota` adlı özel
bir sayaç kullanılabilir. `iota`, bir `const` bloğundaki her satırda sıfırdan başlayıp bir
artar; bir satırın kendi ifadesi yoksa, satır bir önceki satırın ifadesini **olduğu gibi**
tekrar eder ve yalnız `iota`nın değeri güncellenir.

Bunun çözdüğü bir sorun var: bir durum kümesini elle numaralandırmak (`Beklemede = 0`,
`Calisiyor = 1`, `Tamamlandi = 2`, ...) hem tekrar eden bir yazımdır hem de araya yeni bir
durum eklendiğinde bütün sayıların elle kaydırılmasını gerektirir. `iota`, bu numaralandırmayı
satırın **sırasına** bağlıyor; bir satır araya eklendiğinde ondan sonraki bütün değerler
otomatik kayar, hiçbiri elle güncellenmez. Bu, sayıların kendisinin önemli olmadığı, yalnız
**sırasının ve tekilliğinin** önemli olduğu durum kümeleri için kurulmuş bir düzenek.

```go
// sabitler.go — iota ile alti sabit, kacinin degeri kaynakta yazili
package main

import "fmt"

const (
	Beklemede  = iota
	Calisiyor
	Tamamlandi
	Basarisiz  = iota * 100
	Iptal
	ZamanAsimi
)

func main() {
	fmt.Printf("%-12s %d\n", "Beklemede", Beklemede)
	fmt.Printf("%-12s %d\n", "Calisiyor", Calisiyor)
	fmt.Printf("%-12s %d\n", "Tamamlandi", Tamamlandi)
	fmt.Printf("%-12s %d\n", "Basarisiz", Basarisiz)
	fmt.Printf("%-12s %d\n", "Iptal", Iptal)
	fmt.Printf("%-12s %d\n", "ZamanAsimi", ZamanAsimi)
}
```

```
Beklemede    0
Calisiyor    1
Tamamlandi   2
Basarisiz    300
Iptal        400
ZamanAsimi   500
```

**DT8.** Altı sabitin yalnız **ikisinde** (`Beklemede`, `Basarisiz`) bir ifade kaynakta
yazılı; dördünde (`Calisiyor`, `Tamamlandi`, `Iptal`, `ZamanAsimi`) satırda yalnız bir ad
duruyor, değer hiç yazılmıyor. Bu dördünün değeri iki kaynaktan geliyor: satırın kendi
sırasındaki `iota` değeri ve bir önceki satırdan **tekrarlanan** ifade.

Tablo bunu doğruluyor. `Calisiyor` ve `Tamamlandi`, `Beklemede`nin ifadesini (`iota`) tekrar
ediyor ama kendi satırlarındaki `iota` değeriyle (sırasıyla 1 ve 2) hesaplanıyor — bu yüzden
`0, 1, 2` diye artıyorlar. `Iptal` ve `ZamanAsimi` de aynı mantıkla `Basarisiz`in ifadesini
(`iota * 100`) tekrar ediyor, ama kendi `iota` değerleriyle (4 ve 5) çarpılıyor — `300, 400,
500` diye artıyorlar. `iota` bloğun tamamında **tek bir sayaçtır**; grup içinde ikinci bir
ifade göründüğünde sıfırlanmıyor, satır sırasına göre kesintisiz artmaya devam ediyor.

Bu davranış, "yazılmayanın karşılığı" sorusuna kursun şimdiye kadarki en dolaylı yanıtını
veriyor. Bir satırda hiçbir şey yazılmaması, o satırın **rastgele** ya da **boş** bir değer
alacağı anlamına gelmiyor; iki ayrı kural — sıradaki `iota` değeri ve bir önceki ifadenin
tekrarı — birleşip tam olarak tanımlı bir sonuç üretiyor. Yazılmayan dört değerin dördü de bu
yüzden `tanımlı` sınıfına giriyor, `açıkta` değil: sonuç koşumdan koşuma değişmiyor, kaynağı
okuyan biri kâğıt üzerinde de aynı sayılara ulaşabilir.

Bu örnekte her sabit `fmt.Printf` çağrısında kullanıldığı için kullanılmayan değişken reddiyle
karşılaşmıyoruz; ilk derste ölçülen kural burada da geçerli — bir sabit paket düzeyinde
tanımlandığında kullanılmama reddi zaten uygulanmıyor, çünkü Go bu reddi yalnız **yerel**
değişkenlere uyguluyor. Paket düzeyinde tanımlanan bir sabit ya da değişken, hiç okunmasa bile
derlemeyi durdurmaz. Bu ayrım bir sonraki bölümde tekrar karşımıza çıkacak.

Bu tek kural, ilk dersteki reddi hiç çelişmeden daraltıyor. İlk derste ölçülen "kullanılmayan
değişken" reddi, dört ret arasında en sık karşılaşılanıydı ve orada hangi bağlamda geçerli
olduğu hiç sorulmamıştı. Burada görülüyor ki o ret evrensel değil: yerel bir değişkeni
kullanmadan bırakmak reddedilir, paket düzeyinde bir sabiti kullanmadan bırakmak reddedilmez.
Bunun bir nedeni var — paket düzeyindeki bir sabit, aynı paketin başka bir dosyasında, hatta
başka bir paketten dışa aktarıldığında tamamen başka bir dosyada kullanılabilir; derleyici,
tek bir dosyayı okurken bu kullanımın ilerde başka bir yerde olup olmayacağını bilemez. Yerel
bir değişken ise fonksiyonun kendi gövdesiyle sınırlıdır ve derleyici onun kullanılıp
kullanılmadığını aynı bakışta görebilir.

`iota`nın bir de göz ardı etme yolu var: bir satırda ad yerine boş tanımlayıcı `_` yazılırsa,
`iota` yine artar ama o sıraya karşılık gelen hiçbir ad üretilmez. Bu, ilk derste ölçülen
boş tanımlayıcının bir başka kullanımı — orada kullanılmayan bir değeri **isimlendirmeden**
geçmenin yoluydu, burada sıradaki bir numarayı **atlamanın** yolu oluyor. İkisinde de aynı
sözcük, "bunu biliyorum ama bir ada bağlamayacağım" anlamına geliyor.

#### Sınırlayıcı Ölçüm: Kısa Bildirim Her Yerde Çalışmaz

Kısa bildirim, göründüğü kadar evrensel değil. Bunu ölçmek için ilk dersteki derleyici
çağrısına dönüyoruz: aynı satırı iki farklı bağlamda deneyip sonucu sınıflayacağız.

```go
// derleyici.go — derleyiciyi program icinden cagiran yardimci
package main

import (
	"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
}

func sinif(ok bool) string {
	if ok {
		return "derlendi"
	}
	return "derlenmedi"
}
```

Aynı `x := 5` satırını iki farklı bağlamda deneyelim: bir kez bir fonksiyonun dışında, paket
düzeyinde; bir kez bir fonksiyonun içinde.

```go
// sinirlayici.go — kisa bildirim paket duzeyinde reddedilir mi
package main

import "fmt"

func main() {
	paketDuzeyi := `package sinama
x := 5
func F() { _ = x }`
	fonksiyonDuzeyi := `package sinama
func F() { x := 5; _ = x }`

	fmt.Printf("%-24s %s\n", "paket duzeyinde", sinif(derlenirMi(paketDuzeyi)))
	fmt.Printf("%-24s %s\n", "fonksiyon icinde", sinif(derlenirMi(fonksiyonDuzeyi)))
}
```

```
paket duzeyinde          derlenmedi
fonksiyon icinde         derlendi
```

**DT9.** İki kaynak da aynı `x := 5` satırını taşıyor; tek fark, satırın bir fonksiyon
gövdesinin içinde mi dışında mı durduğudur. Derleyici, kısa bildirimi yalnız bir fonksiyon
gövdesinin içinde geçerli bir **ifade** sayıyor; paket düzeyinde beklediği şey `var` ya da
`const` gibi bir bildirim başlığıdır, `:=` değil.

Bu sınır, kısa bildirimin neden "kolay yol" değil, **bağlama özgü bir kısaltma** olduğunu
gösteriyor. Kısa bildirim, bir fonksiyonun içinde art arda birçok değişken tanımlarken `var`
sözcüğünü ve tip adını tekrar tekrar yazmaktan kurtarıyor; paket düzeyinde ise bu kısaltmaya
gerek yok, çünkü paket düzeyindeki bildirimler zaten çoğunlukla tek tek, `var` ya da `const`
ile yazılıyor ve bunların ilklendirme sırası dosya sırasına değil, bağımlılık sırasına göre
çözülüyor — bu çözüm bir sonraki derste temel tipler üzerinden tekrar görülecek.

Bu satırın reddi, ilk dersteki dört retten farklı bir sınıfa ait. Orada reddedilen dört kaynak
sözdizimi açısından geçerliydi, yalnız derleyici belirli bir kalıbı **istemiyordu**. Burada
`x := 5` satırı paket düzeyinde sözdizimi açısından **geçersizdir** — dilin grameri, `:=`
işaretini yalnız bir ifade bağlamında tanıyor, paket düzeyi bir ifade bağlamı değil, bir
bildirim listesidir. Bu, "reddedildi" sınıfının kendi içinde iki alt türü olduğunu gösteriyor:
bir kalıp gramer açısından geçerli ama derleyici tarafından **istenmiyor** olabilir (ilk
dersteki dört ret), ya da kalıbın kendisi o bağlamda **hiç tanımlı değildir** (bu dersteki
ret). İkisi de aynı `derlenmedi` yanıtını üretiyor, ama biri bir kural ihlali, öbürü bir
gramer uyumsuzluğu.

Bu ayrımın pratik sonucu, paket düzeyinde bir değer tanımlamak istendiğinde yazım biçiminin
**değişmesi** gerektiğidir — kısa bildirimden `var`a geçmek bir tercih değil, bir zorunluluktur.
Bir fonksiyonun içinden paket düzeyine taşınan bir satır, yalnız yerini değiştirmekle kalmaz;
yazımını da değiştirmek zorunda kalır.

Bu dersin üç ölçümü — kısa bildirimin tip çıkarımı, `iota`nın sabit doldurması, kısa bildirimin
paket düzeyindeki reddi — aynı sorunun üç ayrı yüzü. Üçünde de yazılmayan bir şey var: bir tip
adı, dört sabit değeri, bir bildirim biçiminin geçerliliği. Üçünde de dil, bu boşluğu rastgele
bırakmıyor: birinde değere bakarak dolduruyor, birinde bir sayaç ve bir tekrar kuralıyla
dolduruyor, birinde ise dolduracak hiçbir şey bulamayıp reddediyor. Kursun ilk iki dersi
"nelerin reddedildiğini" ve "nelerin zorunlu olduğunu" ölçtü; bu ders, reddedilmeyen ve
zorunlu da olmayan bir ara bölgeyi — **çıkarımı** — ölçüyor.

#### Özet

- Kısa bildirim (`:=`), bir değişkenin tipini kaynakta hiç yazmadan, atanan değerin kendi
  tipinden çıkarır.
- `var ad tip = değer` yazımı, çıkarımın sonucunu yazan tarafın **elle** belirlemesine izin
  verir; bu bir dönüşüm değil, bir ilk değer atamasıdır.
- Altı sabitlik bir `iota` dizisinde yalnız ikisinin değeri kaynakta yazılıdır; dördü, satırın
  kendi `iota` değeriyle bir önceki ifadenin tekrarından üretilir.
- `iota`, bir `const` bloğunun tamamında tek bir sayaçtır ve ikinci bir ifade göründüğünde
  sıfırlanmaz.
- Aynı kısa bildirim satırı, bir fonksiyon gövdesinin içinde derlenirken paket düzeyinde
  reddedilir — kısa bildirim yalnız fonksiyon içinde geçerli bir ifadedir.

#### Sonraki Adım

Bu derste bir değişkenin tipi hep bir **değerden** çıkarıldı — `42`den `int`, `3.14`ten
`float64`. Ama bu tiplerin kendileri hiç sorgulanmadı: bir tam sayı taştığında ne oluyor, iki
kayan noktalı değer karşılaştırıldığında ne bekleniyor ve `rune` denen tip aslında neyin
karşılığı? Sıradaki ders temel tipleri, her birinin yazılmayan bir durumda ne yaptığını
ölçerek ele alıyor.

### Temel Tipler

Source: https://academia.sh/tr/kurslar/go-temelleri/temel-tipler

Dört yazılmamışlık aynı çerçeveden geçiriliyor: tipsiz bir sabitin varsayılan tipi int oluyor, int8 sınırını aşan bir toplam -128'e sarıyor, çalışma zamanında toplanan iki float64 değeri 0.3'e eşit çıkmıyor ve bir karakter değişmezinin tipi aslında int32 — rune yalnız bir takma ad. Sınırlayıcı ölçüm: örtük dönüşümün reddi sabitlerde geçerli değil.

Önceki ders bir değişkenin tipini hep bir değerden çıkardı, ama tiplerin kendileri hiç
sorgulanmadı. Bu ders dört soruyu birden soruyor: bağlam vermeden yazılan bir sayının
varsayılan tipi ne, bir tam sayı sınırını aştığında ne oluyor, iki kayan noktalı değer
toplanıp karşılaştırıldığında beklediğimiz sonucu veriyor mu ve `rune` dediğimiz tip aslında
nedir? Dördü de aynı üç sınıfla okunuyor — tanımlı, reddedildi, açıkta — ama bu dördü, kursun
merkezî on beş kalemlik çerçevesinin parçası değil; bu dersin kendi gözlemi.

Programlama Temelleri kursu (M01/K02) temel veri tiplerini, işleç önceliğini ve açık ile örtük
dönüşümü Python'la kurdu. **Bu kavramlar burada tekrar kurulmaz** — farkı tek cümleyle yazmak
yeter: orada tipler çalışma zamanında esnekti ve bir değer taştığında sonuç büyük tam sayıya
genişliyordu; Go'da her tam sayı tipi **sabit genişliktedir** ve taşma genişlemeyle değil,
sarmayla karşılanıyor. Bu ders, o genişliğin sınırında ne olduğunu ölçüyor.

#### Dört Yazılmamışlık, Tek Çerçeve

```go
// temel.go — dort yazilmamislik: varsayilan tip, tasma, kayan nokta karsilastirmasi, rune
package main

import "fmt"

const Sayi = 5

func main() {
	fmt.Printf("%-28s tip=%-8s deger=%v\n", "tipsiz sabitin varsayilani", fmt.Sprintf("%T", Sayi), Sayi)

	var b int8 = 127
	b++
	fmt.Printf("%-28s tip=%-8s deger=%v\n", "tam sayi tasmasi", fmt.Sprintf("%T", b), b)

	var x, y float64 = 0.1, 0.2
	toplam := x + y
	fmt.Printf("%-28s esit_mi=%-6v toplam=%v\n", "kayan nokta karsilastirmasi", toplam == 0.3, toplam)

	c := 'A'
	fmt.Printf("%-28s tip=%-8s deger=%v\n", "karakter degismezi", fmt.Sprintf("%T", c), c)
}
```

```
tipsiz sabitin varsayilani   tip=int      deger=5
tam sayi tasmasi             tip=int8     deger=-128
kayan nokta karsilastirmasi  esit_mi=false  toplam=0.30000000000000004
karakter degismezi           tip=int32    deger=65
```

**DT10.** Dört satır da aynı üç sınıfla (tanımlı, reddedildi, açıkta) okunuyor, ama bu on beş
kalemlik çekirdeğin bir parçası değil — ortak çerçeveye eklenen yeni bir gözlem. Sıfır Değerler
dersindeki 8/4/3 dağılımı bu dört satırı içermiyor.

Dört satırın üçü **tanımlı**, biri **açıkta**. Hiçbiri **reddedilmiyor** — dördü de geçerli
Go kaynağıdır, dördü de derlenir ve çalışır; aralarındaki fark, sonucun kaynaktan mı, sabit bir
kuraldan mı, yoksa çalışma zamanındaki temsilden mi geldiğidir. Sırayla okuyalım.

#### Tipsiz Sabitin Varsayılan Tipi

`Sayi` sabiti `5` değeriyle tanımlanırken hiçbir tip yazılmıyor — `const Sayi = 5`, `const Sayi
int = 5` değil. Go'da böyle bir sabit **tipsizdir**: kendi başına bir `int`, bir `float64` ya
da başka bir tip değildir, yalnız bir sayı değeridir ve kullanıldığı yere göre biçimlenir. `Sayi`
`fmt.Printf`e bir `interface{}` argümanı olarak geçtiğinde, biçimlenecek somut bir tipe ihtiyaç
duyuluyor ve derleyici burada tipsiz sabitin **varsayılan tipini** devreye sokuyor: tam sayı
sabitleri için bu varsayılan `int`tir.

Bu, önceki derste ölçülen kısa bildirimden farklı bir mekanizma. Kısa bildirimde tip, atanan
değerin **kendi** tipinden çıkarılıyordu; burada değerin başlangıçta hiç tipi yok, tip yalnız
bir bağlam onu zorladığında beliriyor. `Sayi`yı bir `float64` değişkenine atasaydık, aynı sabit
hiç dönüşüm yazmadan `float64` olurdu — çünkü tipsiz bir sabit, hedefin izin verdiği her tipe
**kendiliğinden** uyar. Bu ayrım, dersin sınırlayıcı ölçümünde tekrar karşımıza çıkacak.

Varsayılan tip yalnız `int` ile sınırlı değil; her sabit türünün kendi varsayılanı var. Tırnak
içindeki bir sabit varsayılan olarak `string`, `true`/`false` varsayılan olarak `bool`, ondalık
yazılan bir sabit varsayılan olarak `float64` alır. Bunların hiçbiri kaynakta yazılmıyor —
önceki derste kısa bildirimin bir **değerden** tip çıkardığını görmüştük; burada görülen,
değerin kendisinin de, hiçbir bağlam onu zorlamadığı sürece, kendi başına **tipsiz** kalmasıdır.
Tip, ancak bir yere yerleştirildiğinde ortaya çıkıyor.

#### Tam Sayı Taşması: Tanımlı Bir Sarma

`int8` tipi 8 bit genişliğindedir ve işaretli olduğu için -128 ile 127 arasındaki değerleri
tutar. `b`ye 127 verip bir arttırdığımızda, sonuç bu aralığın dışına taşıyor — ama program
çökmüyor, bir hata da üretmiyor. Sonuç **-128** oluyor: sarma, aralığın üst ucundan alt ucuna
dönüyor.

**DT11.** Taşma burada kasıtlı üretiliyor. Go, tam sayı taşmasını bir hata saymıyor; ikiye
tümleyen aritmetiğiyle **tanımlı** bir şekilde sarıyor. Bu, "yazılmayanın karşılığı" sorusuna
üçüncü bir yanıt daha ekliyor: taşmayı önleyecek bir denetim yazılmadı, ama dil bu boşluğa
rastgele bir değer değil, aritmetik olarak öngörülebilir bir değer koyuyor.

Sarmanın kuralı basit: `int8`in üst sınırını (127) bir aşan değer, alt sınırdan (-128) devam
eder. Bu, ikiye tümleyen gösterimin doğal bir sonucu — aynı bit örüntüsü, işaretli okunduğunda
negatif, işaretsiz okunduğunda pozitif bir sayıya karşılık gelir ve toplama işlemi bit
düzeyinde aynı kalır, yalnız yorumlanışı değişir. Derleyicinin burada hiçbir denetim eklememesi
bir ihmal değil — sarma davranışı, donanımın zaten yaptığı bir işlemi olduğu gibi görünür
kılıyor.

Sarmanın öngörülebilir olması, onu **güvenli** yapmıyor. `-128` sonucu, `b`nin gerçek matematik
değerinin (128) hiçbir yaklaşık hâli değil, tamamen farklı bir sayı — üstelik işareti de
değişmiş. Bir hesaplama bu sınırı fark etmeden geçerse, sonucun **yanlış** olduğu hiçbir yerde
işaretlenmez; taşma, ilk derste ölçülen reddedilme ailesinden değil, ölçüm çekirdeğinin
tanımladığı `tanımlı` sınıfından. Dil bir şey koyuyor, ama koyduğu şey her zaman aritmetik
olarak beklenen şey değil.

Bu davranış, tam sayı tiplerinin genişliğinin neden kaynakta **açıkça** seçilmesi gerektiğini
gösteriyor. `int8` yerine daha geniş bir tip (`int32`, `int64`) seçilseydi aynı toplama hiçbir
sarma üretmezdi; sarma, değerin kendisinden değil, seçilen genişlikten kaynaklanıyor. Bu da
tam sayı tipini seçmenin, önceki derste ölçülen kısa bildirimin **dışında** kalan bir karar
olduğunu gösteriyor — kısa bildirim `b := int8(127)` yazılmadıkça hiçbir zaman `int8`e varmaz,
varsayılan olarak her zaman `int`e düşer. `int8` gibi dar bir tip yalnız `var` ile ya da açık
bir dönüşümle seçilebiliyor ve bu seçim taşma sınırını da beraberinde getiriyor.

#### Kayan Nokta Karşılaştırması: Açıkta Kalan Sonuç

Üçüncü satır, `x` ve `y` adlı iki `float64` değişkenini `0.1` ve `0.2` ile ilklendirip
topluyor ve toplamı `0.3`e eşitliyor. Sonuç `false`. Toplamın kendisi de `0.3` değil,
`0.30000000000000004`.

**DT12.** Burada yazdırılan ondalık basamaklar bir ortama bağlı veri değildir — bu, IEEE 754
ikili kesirli gösteriminin **belirlenimci** sonucudur ve her koşumda aynı basamaklara varır.
`0.1` ve `0.2` ikilik tabanda sonlu bir kesir olarak yazılamaz, tıpkı `1/3`ün onluk tabanda
yazılamaması gibi; her ikisi de en yakın `float64` değerine yuvarlanır ve toplamları, gerçek
matematiksel toplamın kendisi değil, o iki yuvarlanmış değerin toplamıdır. Bu yuvarlama Go'ya
özgü değil — `float64` temsili dilin belirtimi değil, IEEE 754 standardının kendisi ve dil bu
temsili olduğu gibi devralıyor.

Bu satır **açıkta** sınıfına giriyor, çünkü dil burada hiçbir şey koymuyor ve derleyici de
hiçbir şeyi reddetmiyor. `==` işleci geçerli bir karşılaştırmadır, derlenir ve çalışır; yalnız
sonucun "matematiksel olarak beklenen" olup olmadığı çalışma zamanındaki temsile bağlı kalıyor.
Kod, ne yaptığını söylüyor — iki `float64` değerini eşitliyor — ama okuyanın kafasındaki
"0.1 artı 0.2, 0.3 eder" beklentisiyle örtüşüp örtüşmeyeceğini önceden söylemiyor.

Burada ilginç bir ayrıntı var: `Sayi` sabitinde gördüğümüz tipsiz sabit mekanizması, bu satırı
da etkileyebilirdi. `0.1 + 0.2 == 0.3` ifadesi, üç tarafı da tipsiz sabit olarak yazılsaydı,
derleyici bu toplamı **derleme zamanında**, keyfi hassasiyetle hesaplardı ve sonuç gerçekten
`true` çıkardı — çünkü sabit aritmetiği ikili yuvarlamaya değil, tam kesir hesabına dayanıyor.
Açıkta kalan davranış, sabitlerin değil, **çalışma zamanı değişkenlerinin** özelliği: `x` ve
`y` birer değişken olduğu için toplamaları çalışma zamanında, önceden yuvarlanmış ikili
değerlerle yapılıyor.

#### Karakterin Tam Sayı Olması

Dördüncü satırda `c := 'A'` yazılıyor. Tek tırnak içindeki bir karakter, Go'da bir **rune
değişmezidir** ve kısa bildirimle tipi çıkarıldığında `int32` çıkıyor — `rune` diye ayrı bir
tip görünmüyor, çünkü `rune`, dilin belirtiminde `int32`nin bir **takma adından** başka bir
şey değil. `%T` her ikisini de aynı adla, `int32` diye yazdırıyor.

Değerin kendisi de bunu doğruluyor: çıktıda `deger=65` yazıyor, `A` değil. `fmt.Printf`in
`%v` biçim doğrulayıcısı, bir değeri kendi tipine göre yazdırıyor ve `int32`nin varsayılan
yazımı ondalık bir sayıdır — karakteri görmek isteseydik `%c` yazmamız gerekirdi. Bu, "rune bir
tam sayıdır" iddiasının yalnız tip sisteminde değil, biçimlendirmede de göründüğü anlamına
geliyor: dil, bir `rune`u özel bir "karakter" kutusunda tutmuyor, sıradan bir sayı gibi
davranıyor ve okunabilir bir karakter olarak görünmesi tamamen biçimlendirme çağrısının
tercihi.

Bu dördüncü satır ilk satırla aynı aileden: ikisi de bir değişmezin **varsayılan tipini**
ölçüyor. Aradaki fark, ilkinde varsayılan tip `int`ken, karakter değişmezinde varsayılan tipin
doğrudan `rune`un kendisi (`int32`) olması. `'A'` yazıldığında dil, bunun bir kod noktası
olduğunu ve kod noktalarının tam sayı olarak temsil edildiğini biliyor; `65` yazmakla `'A'`
yazmak arasındaki fark bu yüzden yalnız **okunurlüktedir** — ikisi de aynı tipte, aynı değerde
sonuçlanıyor.

Bu, `rune` sözcüğünün neden bu kursta hiç çevrilmediğini de açıklıyor: `rune`, Go'nun kendi
anahtar sözcüklerinden biri değil, standart kitaplığın tanımladığı bir **tip takma adıdır** —
tıpkı `byte`ın `uint8`in takma adı olması gibi. Bir takma ad, kaynakta yazıldığında derleyici
tarafından **hemen** gerçek adına çözülüyor; çalışma zamanında `rune` diye ayrı bir tip
etiketi taşınmıyor, `%T` bunu doğruluyor. Bu ders boyunca "karakter" sözcüğünü ölçülmüş bir
şey için kullanmaktan kaçınıyoruz, çünkü tam olarak ölçtüğümüz şey, bir karakterin aslında bir
tam sayı olduğudur — kursun bileşik tipler konusundaki Dizgiler ve Rune'lar dersinde bu ayrım,
bir dizginin neden karakter değil bayt dizisi olduğuyla birlikte derinleşecek.

#### Sınırlayıcı Ölçüm: Örtük Dönüşümün Reddi Sabitlerde Geçerli Değil

İlk derste ölçülen dört retten biri, örtük tip dönüşümünün reddiydi: bir `int32` değişkeni bir
`int64` değişkenine dönüşüm yazılmadan atanamıyordu. Bu kural, **tipsiz sabitler** için
geçerli değil. Ölçmek için ilk dersteki derleyici çağrısına dönüyoruz.

```go
// derleyici.go — derleyiciyi program icinden cagiran yardimci
package main

import (
	"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
}

func sinif(ok bool) string {
	if ok {
		return "derlendi"
	}
	return "derlenmedi"
}
```

```go
// sinirlayici.go — ortuk donusumun reddi sabitlerde gecerli degil
package main

import "fmt"

func main() {
	sabitUyum := `package sinama
func F() { var hedef int64 = 5; _ = hedef }`
	degiskenUyumsuzlugu := `package sinama
func F() { var kaynak int32 = 5; var hedef int64 = kaynak; _ = hedef }`

	fmt.Printf("%-32s %s\n", "tipsiz sabit hedefe uyuyor", sinif(derlenirMi(sabitUyum)))
	fmt.Printf("%-32s %s\n", "ayni degeri tasiyan degisken", sinif(derlenirMi(degiskenUyumsuzlugu)))
}
```

```
tipsiz sabit hedefe uyuyor       derlendi
ayni degeri tasiyan degisken     derlenmedi
```

İki satır da aynı sayısal değeri (`5`) taşıyor ve aynı hedef tipe (`int64`) atanıyor. Birinci
satırda kaynak bir **tipsiz sabittir** — `5` yazımı hiçbir tipe bağlı değil, `int64`e doğrudan
sığar ve derleyici bunu bir dönüşüm değil bir ilk değer ataması sayıyor. İkinci satırda aynı
`5` değeri önce bir `int32` değişkenine atanıyor; bu noktadan sonra `5` artık tipsiz değil,
**`int32` tipinde somutlaşmış** bir değerdir ve `int64`e geçişi ilk derste ölçülen örtük
dönüşüm reddine takılıyor.

Bu, "aynı değer" ile "aynı tip" arasındaki farkı gösteriyor. Derleyicinin ret kararı, taşınan
sayısal değere değil, kaynağın **tipine** bakıyor. Bir tipsiz sabit, somutlaşana kadar hiçbir
genişlik taşımaz; bir değişken ise tanımlandığı anda belirli bir genişliğe kilitlenir. İlk
derste ölçülen kural bu yüzden yeniden yazılmalı: örtük dönüşüm, iki **tipli** değer arasında
reddediliyor; tipsiz bir sabitin hedefe uyması dönüşüm değil, sabitin doğasının bir sonucu.

Bu sınır, dördü olsaydı diye bir soruyla da sınanabilir: `5` yerine `500` yazıp hedefi `int8`
yapsaydık ne olurdu? O durumda tipsiz sabit hedefe **sığmadığı** için derleme yine reddederdi
— ama bu, bir dönüşüm reddi değil, bir **taşma reddidir**: derleyici, bir sabitin değerini
derleme zamanında bildiği için, hedefe sığmayacağını önceden görüp kaynağı hiç derlemez. Bu,
ikinci bölümde ölçülen çalışma zamanı taşmasından (int8'in 127'yi aşması) tamamen farklı bir
mekanizma: orada taşma çalışırken **sarıyordu**, burada taşma derlenirken **reddediliyor**,
çünkü sabitin değeri kaynakta zaten yazılı ve derleyici onu hesaplamak için programı
çalıştırmasına gerek yok.

Dört ölçüm de aynı ayrımı farklı açılardan gösteriyor: bir sabitin tipi kaynakta yazılmadığı
sürece **askıda** kalır ve ancak bir bağlam onu somutlaştırdığında kesinleşir; bir değişkenin
tipi ise tanımlandığı anda kesinleşir ve o andan sonra genişliğiyle birlikte taşınır. İlk
satırdaki varsayılan tip, dördüncü satırdaki karakterin tam sayı oluşu ve sınırlayıcı ölçümdeki
sabit-değişken ayrımı — üçü de bu askıda kalma durumunun farklı görünümleri. Yalnız ikinci ve
üçüncü satır bu aileden ayrılıyor: onlar askıda kalmayla değil, zaten somutlaşmış bir tipin
sınırlarıyla (`int8`in genişliği, `float64`ün ikilik temsili) ilgili.

#### Özet

- Tipsiz bir tam sayı sabitinin bağlam vermeyen bir kullanımda varsayılan tipi `int`tir.
- `int8` sınırını aşan bir toplama, hata üretmeden -128'e sarıyor — taşma tanımlı, ama
  aritmetik olarak beklenen değil.
- Çalışma zamanında toplanan iki `float64` değeri, matematiksel olarak eşit görünen bir
  sabitle karşılaştırıldığında `false` verebiliyor; bu sonuç açıkta kalıyor.
- `rune`, `int32`nin bir takma adıdır; bir karakter değişmezinin varsayılan tipi doğrudan
  budur.
- Örtük tip dönüşümünün reddi yalnız tipli değerler arasında geçerli — tipsiz bir sabit,
  hedef tipe kendiliğinden uyuyor.

#### Sonraki Adım

Bu derste ölçülen dört yazılmamışlığın üçü tanımlı bir karşılık buldu, biri açıkta kaldı. Ama
hiçbiri, bir değişkenin **hiç** değer almadığı durumu sormadı — `var b int8` yazıp hiç değer
vermeseydik, `b` ne olurdu? Sıradaki ders bu soruyu kursun merkezine taşıyor: on beş ayrı
yazılmamışlığı tek bir çerçevede topluyor ve sekizinin tanımlı, dördünün reddedilmiş, üçünün
açıkta olduğunu ölçüyor.

### Sıfır Değerler

Source: https://academia.sh/tr/kurslar/go-temelleri/sifir-degerler

On beş yazılmamışlık tek bir çerçeveden geçiriliyor: sekizi dil tarafından tanımlı bir karşılık buluyor, dördü derleyici tarafından reddediliyor, üçü çalışma zamanına açık kalıyor. Sınırlayıcı ölçüm: nil bir dilime eklenebiliyor ama nil bir sözlüğe yazılamıyor — ikisi de aynı biçimde sıfır değer.

Önceki ders `var b int8` yazıp hiç değer vermeseydik ne olacağını sormadan bitti. Bu soru,
kursun açılışından beri ertelenen asıl soru: bir değişken ilk değeri verilmeden bırakıldığında
dil orada ne buluyor? Bu ders bu soruyu tek bir örnekte değil, on beş ayrı yerde soruyor ve
üç sınıftan birine yazıyor — **tanımlı**, dil yazılmayanın yerine bir değer koyuyor;
**reddedildi**, derleyici yazılmamış olmayı kabul etmiyor; **açıkta**, sonuç çalışma zamanına
kalıyor. Kursun geri kalanı boyunca atıf yapılacak sayılar burada üretiliyor.

Programlama Temelleri kursu (M01/K02) bir değişkenin bir adla bağlanmasını Python'la kurdu;
orada bir ad, bir değer verilmeden var olamazdı. **Bu kavram burada tekrar kurulmaz** — farkı
tek cümleyle yazmak yeter: Go'da bir değişken değer verilmeden de var olabiliyor, çünkü tip
sistemi her tipe önceden bir **sıfır değer** atamış durumda. Bu dersin ölçtüğü, o sıfır
değerin her tipte ne olduğu ve her durumda gerçekten "orada" olup olmadığıdır.

#### Kurgu

Go'da bir programı okurken en çok işe yarayan soru, yazılanın ne yaptığı değil, yazılmayanın ne
olduğudur. İlk değeri verilmeyen her değişkenin tanımlı bir değeri var; kullanılmayan bir değişken
derlemeyi durduruyor; olmayan bir anahtar iki yanıt birden dönüyor; hiç argüman verilmeyen bir
değişken argüman listesi de boş bir dilim oluyor. Aynı dilde, yazılmayan bazı şeylerin karşılığı
hiç yok ve program çalışırken duruyor.

Ölçüm bu üçlüyü tek bir soruya indiriyor: her yazılmamışlık için, dil bir şey mi koyuyor,
derleyici mi reddediyor, yoksa sonuç açıkta mı kalıyor? Yanıtı bulmanın ikinci yarısı
derleyiciyi **program içinden** çağırıyor: bir kaynak parçası geçici bir pakete yazılıyor,
derleyici koşturuluyor ve yalnız geçip geçmediği okunuyor — hata metni hiç okunmuyor, çünkü
ölçülen şey "neden reddedildi" değil, "reddedildi mi".

#### Çekirdek: Üç Sınıfa Ayıran Bir Gözlemci

```go
// eksik.go — yazilmayan sey ne oluyor: dil tanimli bir sey mi koyuyor, derleyici mi reddediyor, yoksa acikta mi kaliyor
package main

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

var tanimli, reddedildi, acikta int

func gozle(ad, sinif, kanit string) {
	switch sinif {
	case "tanimli":
		tanimli++
	case "reddedildi":
		reddedildi++
	default:
		acikta++
	}
	fmt.Printf("%-38s %-11s %s\n", ad, sinif, kanit)
}

func ozet() {
	fmt.Printf("yazilmayan: %d tanimli, %d reddedildi, %d acikta\n", tanimli, reddedildi, acikta)
}

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
}

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}
```

**DT13.** `gozle`, her gözlemi bir sınıfa yazan tek giriş noktasıdır; sayaçları tutan üç
paket düzeyi değişken (`tanimli`, `reddedildi`, `acikta`) yalnız burada artıyor, böylece
`ozet` çağrıldığında toplamlar hep gözlemlerle tutarlı kalıyor.

**DT14.** `panikliyorMu`, bir işlevi çalıştırıp panikleyip paniklemediğini `recover` ile
yakalıyor ve yalnız `panik=true`/`panik=false` döndürüyor — hiçbir yığıt izi basılmıyor. Bu,
kursun panik ölçen her yerinde geçerli tek yöntem.

Bu iki yardımcı işlev, dersin ölçtüğü üç sınıfın her birine bir araç sağlıyor: `gozle` ve
`ozet` sınıflandırmayı ve toplamı tutuyor, `panikliyorMu` açıkta kalan davranışı yakalıyor,
`derlenirMi` (bir önceki bölümde tanımlı) reddi ölçüyor. Üçü birlikte, on beş satırlık ölçümün
gövdesini tek bir çağrı biçimine indiriyor: her satır `gozle`ye bir ad, bir sınıf ve bir kanıt
veriyor, geri kalan her şeyi bu üç yardımcı hallediyor.

#### On Beş Yazılmamışlık

```go
// olcum.go — on bes yazilmamislik, uc sinifa bolunur
package main

import "fmt"

type Kayit struct {
	Ad    string
	Sayi  int
	Etkin bool
}

func adliDonus() (sonuc int, hata error) { return }

func degisken(sayilar ...int) []int { return sayilar }

func main() {
	fmt.Println("-- yazilmayanin karsiligi --")

	var tamsayi int
	gozle("ilk deger verilmeyen tam sayi", "tanimli", fmt.Sprint(tamsayi))

	var dilim []int
	gozle("ilk deger verilmeyen dilim", "tanimli",
		fmt.Sprint("nil=", dilim == nil, " uzunluk=", len(dilim), " eklenebilir=", append(dilim, 1)))

	var esleme map[string]int
	gozle("ilk deger verilmeyen eslemeden okuma", "tanimli", fmt.Sprint(esleme["yok"]))

	yazmaPanikledi := panikliyorMu(func() { esleme["yeni"] = 1 })
	gozle("ilk deger verilmeyen eslemeye yazma", "acikta", fmt.Sprint("panik=", yazmaPanikledi))

	var isaretci *Kayit
	okumaPanikledi := panikliyorMu(func() { _ = isaretci.Sayi })
	gozle("ilk deger verilmeyen isaretciden okuma", "acikta", fmt.Sprint("panik=", okumaPanikledi))

	var kayit Kayit
	gozle("yazilmayan yapi alanlari", "tanimli", fmt.Sprintf("%q %d %t", kayit.Ad, kayit.Sayi, kayit.Etkin))

	s, h := adliDonus()
	gozle("adlandirilmis donusun yazilmamasi", "tanimli", fmt.Sprint(s, " ", h == nil))

	gozle("degisken argumana hic arguman", "tanimli",
		fmt.Sprint("nil=", degisken() == nil, " uzunluk=", len(degisken())))

	kaynak := []int{10, 20, 30, 40}
	gozle("dilim sinirlarinin yazilmamasi", "tanimli", fmt.Sprint(kaynak[:], " ", kaynak[1:], " ", kaynak[:2]))

	deger, varMi := esleme["yok"]
	gozle("olmayan anahtarin iki yanit vermesi", "tanimli", fmt.Sprint(deger, " ", varMi))

	metin := "çay"
	gozle("dizgide karakter yerine bayt", "acikta",
		fmt.Sprint("uzunluk=", len(metin), " ilk=", metin[0], " rune=", len([]rune(metin))))

	gozle("kullanilmayan degisken", siniflaKaynak(`package sinama
func F() { x := 1 }`), "")

	gozle("kullanilmayan ice aktarim", siniflaKaynak(`package sinama
import "fmt"
func F() {}`), "")

	gozle("donus degerinin yazilmamasi", siniflaKaynak(`package sinama
func F() int { }`), "")

	gozle("ortuk tip donusumu", siniflaKaynak(`package sinama
func F() { var a int32 = 1; var b int64 = a; _ = b }`), "")

	ozet()
}

func siniflaKaynak(kaynak string) string {
	if derlenirMi(kaynak) {
		return "acikta"
	}
	return "reddedildi"
}
```

```
-- yazilmayanin karsiligi --
ilk deger verilmeyen tam sayi          tanimli     0
ilk deger verilmeyen dilim             tanimli     nil=true uzunluk=0 eklenebilir=[1]
ilk deger verilmeyen eslemeden okuma   tanimli     0
ilk deger verilmeyen eslemeye yazma    acikta      panik=true
ilk deger verilmeyen isaretciden okuma acikta      panik=true
yazilmayan yapi alanlari               tanimli     "" 0 false
adlandirilmis donusun yazilmamasi      tanimli     0 true
degisken argumana hic arguman          tanimli     nil=true uzunluk=0
dilim sinirlarinin yazilmamasi         tanimli     [10 20 30 40] [20 30 40] [10 20]
olmayan anahtarin iki yanit vermesi    tanimli     0 false
dizgide karakter yerine bayt           acikta      uzunluk=4 ilk=195 rune=3
kullanilmayan degisken                 reddedildi  
kullanilmayan ice aktarim              reddedildi  
donus degerinin yazilmamasi            reddedildi  
ortuk tip donusumu                     reddedildi  
yazilmayan: 8 tanimli, 4 reddedildi, 3 acikta
```

Son dört satır, ilk derste ayrıntılı ölçülen dört rettir; burada listenin tamamlayıcısı olarak yer
alıyor ve aynı sınıfa (`reddedildi`) düşüyor. Geri kalan on bir satır bu derse özgü ve sekiz ayrı
tipi kapsıyor: tam sayı, dilim, sözlük, işaretçi, yapı, isimlendirilmiş dönüş değeri, değişken
sayıda argüman, dizgi. Her satır aynı soruyu tek bir tipe soruyor — bu tipin ilk değeri
verilmediğinde ne oluyor — ve yanıtı `gozle`ye üç parçayla teslim ediyor: bir ad, bir sınıf, bir
kanıt. Kanıt sütunu, iddiayı doğrulayan gerçek değerleri taşıyor; bir sonraki bölüm bu kanıtları
tek tek okuyor.

#### Sayıların Okunması

**Birinci iddia: Go'da "başlatılmamış" diye bir durum yok.** On beş yazılmamışlığın **sekizinde**
dil yazılmayanın yerine tanımlı bir şey koyuyor: bir tam sayının sıfır değeri `0`, bir dilimin
sıfır değeri `nil` ama uzunluğu `0` ve eklenebilir, bir sözlükten okuma `0` dönüyor, bir yapının
alanları kendi sıfır değerlerini taşıyor, adlandırılmış bir dönüş yazılmadığında `0` ve `nil`
dönüyor, değişken sayıda argümana hiç argüman verilmediğinde parametre `nil` bir dilim oluyor,
dilim sınırları yazılmadığında baştan ve sona kadar uzanıyor, olmayan bir anahtar `0` ve
`false` dönüyor. Sekizi de bir kolaylık değil bir sözleşme: okuyan taraf ne bulacağını
kaynağa bakmadan bilir.

Bu sekizinin ortak yanı, sıfır değerin **tip başına** farklı görünmesi ama her zaman
öngörülebilir olması. Bir tam sayı için sıfır değer sayısal sıfırdır; bir dizgi için boş
dizgidir (`""`); bir mantıksal değer için `false`tur; bir işaretçi, dilim, sözlük ya da işlev
için `nil`dir. Dördü de aynı sözcükle (`nil`) anılmasa da aynı fikri taşıyor: "henüz hiçbir
şeye bağlı değil". `nil`in kendisi tek bir değer değildir — bir işaretçinin `nil`i bellekte
hiçbir adresi göstermediği, bir dilimin `nil`i hiçbir alt diziye sahip olmadığı, bir sözlüğün
`nil`i hiçbir depolama alanı ayrılmadığı, bir işlevin `nil`i çağrılabilecek hiçbir kod
bulunmadığı anlamına geliyor. Dördü de aynı sözcükle yazdırılıyor ama dördü de kendi tipinin
içinde ayrı bir yokluğu temsil ediyor.

**İkinci iddia: derleyicinin reddettiği dört şeyin dördü de başka dillerde uyarı düzeyindedir.**
Kullanılmayan değişken, kullanılmayan içe aktarım, dönüş değerinin yazılmaması, örtük tip
dönüşümü — dördü de kaynağı derlenmez yapıyor. Reddetmek bir katılık gösterisi değil,
yazılmayanın karşılığını **daraltma** kararı: dil bu dört durumda "tanımlı bir şey koymak"
yerine "hiç izin vermemek"i seçiyor.

Bu dördü, ilk sekizle karşılaştırıldığında ilginç bir yer değiştirme gösteriyor. İlk sekizde
dil, yazılmayan bir şeye rağmen kaynağı çalıştırmaya izin veriyor ve boşluğu kendi dolduruyor.
Bu dört durumda ise dil, boşluğu doldurmak yerine kaynağı hiç çalıştırmıyor. İkisi de
"yazılmayanın karşılığı" olsa da, biri çalışma zamanına taşınan bir değer, öbürü derleme
zamanında kapanan bir kapı.

**Üçüncü iddia: tanımlılık okuma yönünde çalışıyor, yazma ve yorumlama yönünde bitiyor.**
Açıkta kalan üç durumun üçü de bunu gösteriyor. İlk değeri verilmeyen bir sözlükten **okumak**
tanımlıdır ama ona **yazmak** programı durduruyor — panik. İlk değeri verilmeyen bir işaretçi
tanımlı bir değerdir (`nil`) ama **okunduğunda** durur — yine panik. Bir dizginin uzunluğu
tanımlıdır ama indekslendiğinde gelen şey karakter değil **bayttır** — `"çay"` dört bayt, üç
`rune` ve ilk bayt `195` (bir çok baytlı kodlamanın ilk parçası, tek başına bir harf değil).

#### Sınırlayıcı Ölçüm: "Tanımlı" Kullanışlı Demek Değildir

Sekiz tanımlı satırın hepsi aynı derecede kullanışlı değil. İkinci satır (`nil` dilim) ile üçüncü
satır (`nil` sözlükten okuma) karşılaştırılırsa fark netleşiyor: ikisi de ilk değeri verilmemiş ve
ikisi de `nil`. Ama `nil` dilime `append` çalışıyor — çıktıda `eklenebilir=[1]` görünüyor — `nil`
sözlüğe yazma ise panikliyor. Aynı sözcük (`nil`), aynı sınıf (`tanımlı`), iki farklı sonuç.

**DT15.** Fark, `nil`in kendisinde değil, dilin bu iki tip için verdiği **ayrı sözde**. Bir dilim,
`nil` olsa bile bir uzunluk (`0`) ve bir kapasite (`0`) taşıyan bir başlıktır; `append` bu başlığa
yeni bir alt dizi ayırıp genişleyebilir, çünkü genişleme dilimin zaten sahip olduğu bir
davranıştır. Bir sözlük ise `nil` olduğunda hiçbir depolama alanı içermiyor; okuma bu boşlukla
başa çıkabiliyor (aranan anahtar zaten yoktur, sıfır değeri dönülür) ama yazma yeni bir girdi için
yer ayırmayı gerektiriyor ve `nil` sözlüğün ayıracak hiçbir yeri yok. "Tanımlı" sözcüğü ikisinde
de doğru, ama **neyin** tanımlı olduğu tipten tipe değişiyor.

Bu, sekiz tanımlı satırı okurken akılda tutulması gereken bir uyarı: bir değerin sıfır değeri
olması, o değerin **her işlemi** kaldırabileceği anlamına gelmiyor. Hangi işlemlerin sıfır
değerle çalıştığı, kursun sıradaki konusunda dilim ve sözlük tipleri ayrı ayrı ele alınırken
tekrar ölçülecek.

Aynı sınırlayıcı okuma, açıkta kalan ikinci satırda da bir kez daha görünüyor. İşaretçinin sıfır
değeri de `nil`dir ve `nil` dilim gibi bazı işlemleri kaldırıyor — bir `nil` işaretçiyi başka bir
işaretçiyle karşılaştırmak ya da yeniden atamak sorunsuz çalışıyor. Ama alan okumak
(`isaretci.Sayi`) çalışmıyor, çünkü okunacak bir alan için önce bir yapı örneğinin var olması
gerekiyor ve `nil` işaretçi hiçbir örneği göstermiyor. Üç tip — dilim, sözlük, işaretçi — üçü de
`nil`i sıfır değeri olarak taşıyor ama üçü de kendi davranış kümesini kendisi tanımlıyor; tek bir
"nil kuralı" yok, tip başına ayrı bir sözleşme var.

#### Kör Nokta: Redd Nedeni Ölçülmüyor

Dört ret satırı `derlenirMi`nin `true`/`false` yanıtına dayanıyor ve hata metni hiç okunmuyor.
Bunun bir bedeli var: bir kaynak, ölçülmek istenenden **başka** bir nedenle de reddedilmiş
olabilir ve ölçüm bunu ayırt edemez. Bu yüzden kural şu — reddedilen her kaynak parçası, tek
bir yazılmamışlık dışında derlenebilir olmalı; kullanılmayan değişken örneğinde `x := 1`
dışında hiçbir sorun yok, dönüş değeri örneğinde `func F() int { }` dışında hiçbir sorun yok.
Bu koşul elle kurulup doğrulandı; ölçüm aracı bunu kendisi denetlemiyor.

**DT16.** Bu, ölçümün yönteminden gelen bilinçli bir sınırlama, bir gözden kaçırma değil.
`derlenirMi` yalnız `cmd.Run()`in çıkış durumunu okuyor; hata metnini okumamak, hem sürüme
bağlı veri yasağını hem de "neden değil, ne" sorusunu koruyor. Bedeli, bu dersteki dört
kaynağın **elle** doğrulanmış olmasıdır — her biri yazılırken tek bir değişiklik denendi ve
geri kalan her şeyin sabit tutulduğu kontrol edildi. Kursun geri kalanında derleyici reddi
ölçülen her yerde aynı disiplin sürüyor.

Sekiz satırlık tanımlı grubun da benzer bir kör noktası var: `gozle` işlevi, kendisine verilen
kanıtı olduğu gibi yazdırıyor, doğruluğunu sınamıyor. Bir satırın gerçekten "tanımlı" sınıfına
ait olup olmadığı, kod yazılırken elle karar veriliyor — çerçeve yalnız bu kararı **saydırıyor**,
kendisi üretmiyor. Kâhin (hangi yazılmamışlığın hangi sınıfa girdiği bilgisi) programın dışında,
bu dersi yazan tarafın elinde.

#### Özet

- On beş yazılmamışlığın **8'i tanımlı, 4'ü reddedildi, 3'ü açıkta** — kursun bağlayıcı
  dağılımı burada üretildi.
- Go'da "başlatılmamış" diye bir durum yok: sekiz tanımlı satırın her biri, dilin yazılmayanın
  yerine koyduğu somut bir değer taşıyor.
- Derleyicinin reddettiği dört şey, çalışma zamanında zararsız olsa da yazılmayanın karşılığını
  daraltma kararıyla derlemeyi durduruyor.
- Tanımlılık okuma yönünde çalışıyor, yazma ve yorumlama yönünde bitiyor: `nil` sözlükten
  okumak tanımlı, yazmak panik; dizginin uzunluğu tanımlı, indekslemesi bayt veriyor.
- `nil` dilim ile `nil` sözlük aynı sözcüğü taşısa da aynı işlemleri kaldırmıyor — `nil` dilime
  eklenebiliyor, `nil` sözlüğe yazılamıyor.

#### Sonraki Adım

Sıfır değerler, bir değişkenin **hiç dokunulmadığında** ne olduğunu ölçtü. Ama bir program
neredeyse hiçbir zaman değişkenlere dokunmadan durmuyor — dallanıyor, döngü kuruyor, bir
durumdan diğerine geçiyor. Sıradaki ders denetim akışına bakıyor: `for` neden koşulsuz
yazılabiliyor, `switch` neden ifadesiz yazılabiliyor ve bir dalın sonunda yazılmayan `break`
neyin yerini tutuyor?

### Denetim Akışı

Source: https://academia.sh/tr/kurslar/go-temelleri/denetim-akisi

Beş biçim aynı soruyla ölçülüyor — koşulsuz for, tek koşullu for, ifadesiz switch, else'siz erken dönüş, kendiliğinden düşmeyen switch dalları — ve beşi de yazılmayan parçayı tanımlı bir karşılıkla dolduruyor. Sınırlayıcı ölçüm: yazılmayan break gerekmiyor, ama düşmeyi isteyen kod fazladan bir sözcük (fallthrough) yazmak zorunda.

Önceki ders on beş yazılmamışlığı ölçtü ama hiçbiri bir denetim yapısının **içinde**
değildi — hepsi bir değişkenin, bir dönüşün ya da bir argümanın yokluğuyla ilgiliydi. Bu ders
aynı soruyu denetim akışına taşıyor: `for` neden bazen koşulsuz yazılabiliyor, `switch` neden
bazen ifadesiz yazılabiliyor ve bir dalın sonunda yazılmayan `break` neyin yerini tutuyor?

Programlama Temelleri kursu (M01/K02) koşullu dallanmayı ve döngü denetimini Python'la kurdu;
orada döngü ve dallanma için ayrı anahtar sözcükler vardı (`while`, `for`, `if`/`elif`/`else`)
ve her birinin kendi sözdizimi sabitti. **Bu kavramlar burada tekrar kurulmaz** — farkı tek
cümleyle yazmak yeter: Go, döngü için **tek** bir anahtar sözcük (`for`) kullanıyor ve bu
sözcüğün hangi klasik döngü biçimine karşılık geldiği, kaynakta hangi bölümlerin yazılıp
yazılmadığına bağlı. Bu ders o bağlılığı ölçüyor.

#### Beş Biçim, Tek Soru

Go'nun denetim akışı iki anahtar sözcüğe dayanıyor: `for` ve `switch` (`if` üçüncüsü, ama
onda yazılmayan tek şey `else`). Diğer dillerde ayrı sözcüklerle karşılanan "sonsuz döngü",
"koşullu döngü" ve "sayaçlı döngü" ayrımı, Go'da tek bir `for` sözcüğünün **hangi bölümlerinin
yazıldığına** indirgeniyor. Beş ayrı biçimi aynı çerçevede koşturuyoruz: her biri bir şeyi
yazmadan bırakıyor, dil o boşluğu kendi dolduruyor.

```go
// akis.go — bes bicim, yazilmayan parcayi tanimli bir karsilikla dolduruyor
package main

import "fmt"

func siniflandir(x int) string {
	if x < 0 {
		return "negatif"
	}
	return "negatif degil"
}

func main() {
	sayac := 0
	for {
		sayac++
		if sayac == 5 {
			break
		}
	}
	fmt.Printf("%-28s %s\n", "kosulsuz for (durana kadar)", fmt.Sprint("sayac=", sayac))

	i, toplam := 0, 0
	for i < 5 {
		toplam += i
		i++
	}
	fmt.Printf("%-28s %s\n", "tek kosullu for", fmt.Sprint("toplam=", toplam, " son_i=", i))

	puan := 72
	var not string
	switch {
	case puan >= 90:
		not = "AA"
	case puan >= 70:
		not = "BA"
	default:
		not = "FF"
	}
	fmt.Printf("%-28s %s\n", "ifadesiz switch", fmt.Sprint("not=", not))

	fmt.Printf("%-28s %s\n", "else'siz erken donus", fmt.Sprint(siniflandir(-3), " / ", siniflandir(3)))

	sonuc := ""
	switch 2 {
	case 1:
		sonuc += "bir"
	case 2:
		sonuc += "iki"
	case 3:
		sonuc += "uc"
	}
	fmt.Printf("%-28s %s\n", "switch dusmuyor", fmt.Sprint("sonuc=", sonuc))
}
```

```
kosulsuz for (durana kadar)  sayac=5
tek kosullu for              toplam=10 son_i=5
ifadesiz switch              not=BA
else'siz erken donus         negatif / negatif degil
switch dusmuyor              sonuc=iki
```

Beş satırın her biri, gövdesinden bağımsız okunabilecek bir sayıya dayanıyor. `toplam=10`,
`0+1+2+3+4`ün toplamı — döngü beş kez (`i` sıfırdan dörde) çalıştığını, `i < 5` koşulu
sağlandığı sürece devam ettiğini doğruluyor; `son_i=5` ise döngünün, koşul artık sağlanmadığı
an durduğunu gösteriyor. `not=BA`, 72 puanın `puan >= 70` dalına düştüğünü, `puan >= 90`
dalının hiç denenmediğini kanıtlıyor. Beşi de birbirinden bağımsız kanıtlar, ama hepsi aynı
soruya yanıt veriyor: yazılmayan parça neyle dolduruluyor?

**DT17.** `for`ın üç bölümü de (ilklendirme, koşul, sonlandırma) isteğe bağlıdır. İlk satırda
hiçbiri yazılmıyor; derleyici yazılmayan koşulun yerine **örtük `true`** koyuyor, bu yüzden
döngü kendiliğinden durmuyor — `sayac`, `break`e kadar sınırsız artmaya devam edeceğini
gösteriyor, biz onu beşte durduruyoruz. İkinci satırda yalnız koşul yazılıyor; ilklendirme ve
sonlandırma yazılmadığında dil onları **atlıyor**, `i`nin döngüden önce tanımlanmış ve
gövde içinde elle artırılmış olması yeterli.

İki `for` biçimi de aynı iddiayı farklı uçlardan gösteriyor: yazılmayan bir bölüm, dilin
"bu bölüm yok sayılsın" demesiyle karşılanıyor — koşul yoksa hep doğru kabul ediliyor,
ilklendirme ve sonlandırma yoksa hiç çalıştırılmıyor. Üçü de aynı `for` sözcüğünün parçası;
hangi bölümlerin yazıldığı, döngünün hangi klasik biçime (sonsuz, koşullu, üç adımlı) karşılık
geldiğini belirliyor.

Bu, `for`ın tek bir sözdizim ağacı taşıdığı, ama o ağacın üç dalının da isteğe bağlı olduğu
anlamına geliyor. Üç adımlı biçim (`for i := 0; i < 5; i++ { }`) hiçbir bölümü atlamaz ve
kaynakta en açık hâliyle durur; ikinci satırdaki tek koşullu biçim, ilklendirme ile
sonlandırmayı dışarıya, gövdenin öncesine ve içine taşır — `i, toplam := 0, 0` döngüden önce,
`i++` gövdenin son satırında. Derleyici için bu iki yazım aynı makine kodunu üretir; aradaki
fark tamamen okuyan tarafın neyi bir bakışta göreceğidir. Üç adımlı biçimde döngünün sınırları
tek satırda toplanır; tek koşullu biçimde sınırları görmek için gövdenin tamamını okumak
gerekir.

Bu ayrımın bir bedeli var: birinci satırdaki `sayac` döngüden önce sıfırlanıyor ve döngü
içinde artıyor, ama döngünün ne zaman duracağı bilgisi ne başlıkta ne de tek bir satırda —
gövdenin ortasındaki bir `if` bloğunda gizli. Koşulsuz `for`, en esnek biçimdir çünkü durma
koşulunu **hiçbir yere** sabitlemez; bu esneklik, durma koşulunu bulmak için gövdenin tamamını
okumayı gerektirir.

Bu esnekliğin bir bedeli olduğu kadar bir nedeni de var: bazı döngülerin durma koşulu, tek bir
karşılaştırmayla ifade edilemeyecek kadar karmaşıktır — birden fazla durum birlikte
sağlandığında ya da bir dış kaynaktan (bir kanaldan, bir okuyucudan) gelen bir sinyale bağlı
olarak durur. Böyle bir döngü için başlıkta tek bir koşul yazmak, gövdenin gerçek durma
mantığını başlığa **sığdırmaya** çalışmak olurdu; koşulsuz `for`, durma mantığını olduğu gibi
gövdeye bırakmanın yolu. Üç adımlı ve tek koşullu biçimler, durma koşulu tek bir karşılaştırmaya
indirgenebildiğinde tercih ediliyor; koşulsuz biçim, indirgenemediğinde.

**DT18.** Üçüncü satırda `switch` bir ifade almadan yazılıyor. Bir `switch` ifadesi
yazılmadığında derleyici onu **örtük `true`** ile karşılaştırıyor; her `case` de kendi
başına bir mantıksal ifadeye dönüşüyor. `puan >= 70` doğru olduğu için `not`, `"BA"` oluyor —
tam olarak bir `if`/`else if` zincirinin yapacağı şey, yalnız `switch` sözdizimiyle.

Bu, `switch`in Go'da bir "eşitlik" yapısından çok bir "dallanma" yapısı olduğunu gösteriyor.
Bazı dillerde `switch`, yalnız tek bir değeri sabit değerlerle eşitleyebilir; Go'da bir ifade
yazılmadığında `case`ler serbestçe mantıksal koşullar taşıyabiliyor ve bu koşullar sırayla
denenip ilk doğru olan çalıştırılıyor — tıpkı bir `if`/`else if` zincirinde olduğu gibi. Yazılan
`switch { }` ile `switch true { }` arasında hiçbir fark yok; ikincisi yalnız ilkinin yazılmayan
kısmını açıkça gösteriyor.

Bu, `switch`in iki ayrı kullanımı tek bir sözdizimde birleştirdiğini gösteriyor. Bir ifade
yazıldığında (`switch puan { case 90: ... }`), her `case` o ifadeyle **eşitlenen** bir sabit
listesi taşıyabiliyor — çoklu değer eşleşmesi bunun bir örneği, kursun ilerleyen bir dersinde
tekrar görülecek. İfade yazılmadığında ise her `case` kendi başına tam bir mantıksal ifade
taşıyor ve eşitlik değil, doğruluk sınanıyor. Aynı anahtar sözcük, yazılan tek bir parçaya
(ifadenin kendisi) bağlı olarak iki farklı dallanma biçimine dönüşüyor.

Beşinci
satırda ise farklı bir yazılmamışlık ölçülüyor: hiçbir `case` sonunda `break` yok, ama
`sonuc`, yalnız eşleşen dalın (`case 2`) değerini taşıyor — `case 3`ün gövdesi hiç
çalışmıyor. Go'da bir `case`in sonu, dalın kendiliğinden **kapanması** demek; yazılmayan
`break`in karşılığı, düşmenin **hiç olmaması**dır.

Dördüncü satır farklı bir aileden: `else` hiç yazılmadan, `if` bloğu bir `return` ile bitiyor
ve kalan kod sanki bir `else` bloğuymuş gibi çalışıyor. `siniflandir(-3)`, `if` dalına girip
`"negatif"` döndürüyor; `siniflandir(3)`, `if` dalına hiç girmeden ikinci satıra düşüp
`"negatif degil"` döndürüyor. Yazılmayan `else`nin karşılığı, kontrolün `if` bloğundan çıkan
her yolun (bir `return` ile ya da hiç girmeden) aynı yere, işlevin geri kalanına
**varmasıdır**.

Bu biçim, Go kaynaklarında "koruma cümlesi" (guard clause) diye anılan bir düzenek — bir
işlevin başında istisnai durumları erken `return` ile eleyip, geri kalan gövdeyi tek bir iç
içe geçmeden yazmanın yolu. `if x < 0 { return "negatif" }` satırından sonra `else` yazılsaydı,
işlevin geri kalanı bir kademe daha içeri kayardı; `else` yazılmadığı için ikinci `return`
işlevin ana gövdesiyle aynı girinti düzeyinde kalıyor. Yazılmayan `else`nin karşılığı bu yüzden
yalnız bir davranış değil, aynı zamanda bir **biçim** kazancı: gövde, iç içe geçmiş bloklar
yerine düz bir sırayla okunuyor.

Bu düzenek, işlev tek bir istisnai durum yerine birden fazlasını elediğinde daha da belirgin
oluyor. Art arda birkaç koruma cümlesi yazıldığında (`if a { return }`, `if b { return }`, `if
c { return }`), her biri kendi `else`sini yazsaydı gövde her satırda bir kademe daha içeri
kayardı; `else` hiç yazılmadığında bütün koruma cümleleri aynı girinti düzeyinde yan yana
durur ve işlevin "asıl işi" en sonunda, en az girintili satırda kalır. Yazılmayan `else`,
burada tek bir satırın değil, işlevin **tamamının** okunurluğünü etkiliyor.

#### Sınırlayıcı Ölçüm: Yazılmayan Break, Yazılması Gereken Fallthrough

Beşinci satırdaki "düşmeme" davranışı kolaylık gibi görünüyor — bir sözcük (`break`) yazmaktan
kurtulduk. Ama tersini istediğimizde, yani bir dalın bilerek bir sonrakine düşmesini
istediğimizde, dil bunu **bedelsiz** vermiyor.

Bu ölçümün önemi, "yazılmayan bir şey her zaman en kısa yol mu" sorusuna verdiği yanıtta
yatıyor. Beşinci satırda gördüğümüz üzere, hiçbir `case` `break` yazmadı ve yine de yalnız
eşleşen dal çalıştı — bu bir kazanç gibi göründü. Ama bu kazanç, "düşme" davranışının dilde hiç
var olmadığı anlamına gelmiyor; yalnız **varsayılan olmadığı** anlamına geliyor. Aşağıdaki iki
`switch`, aynı üç dalı taşıyor ve aynı değeri (`2`) sınıyor; aralarındaki tek fark, ikincisinin
`case 2` gövdesine bir sözcük eklemesi.

```go
// sinirlayici.go — yazilmayan break gerekmiyor, ama dusmeyi isteyen kod fazladan sozcuk yaziyor
package main

import "fmt"

func main() {
	yaziliDegil := ""
	switch 2 {
	case 1:
		yaziliDegil += "bir"
	case 2:
		yaziliDegil += "iki"
	case 3:
		yaziliDegil += "uc"
	}

	yazili := ""
	switch 2 {
	case 1:
		yazili += "bir"
	case 2:
		yazili += "iki"
		fallthrough
	case 3:
		yazili += "uc"
	}

	fmt.Printf("%-28s %s\n", "break yazilmadi", fmt.Sprint("sonuc=", yaziliDegil))
	fmt.Printf("%-28s %s\n", "fallthrough yazildi", fmt.Sprint("sonuc=", yazili))
}
```

```
break yazilmadi              sonuc=iki
fallthrough yazildi          sonuc=ikiuc
```

**DT19.** İki `switch` da aynı değeri (`2`) sınıyor ve aynı üç dalı taşıyor; tek fark, ikinci
`switch`in `case 2` gövdesine `fallthrough` sözcüğünün eklenmesi. Birinci satırda sonuç yalnız
`"iki"` — `case 2` çalışıyor; `case 3`e hiç geçilmiyor. İkinci satırda sonuç `"ikiuc"` — açıkça
istenen bir düşme, `case 2`nin ardından `case 3`ü de çalıştırıyor.

Bu, "yazılmayanın karşılığı" sorusuna bir asimetri ekliyor. Varsayılan davranış (düşmeme)
hiçbir sözcük gerektirmiyor; varsayılanın **tersi** (düşme) ise adı konmuş, tek amaçlı bir
anahtar sözcük gerektiriyor. Bu asimetri kasıtlı: bir `case`in gövdesini okuyan biri, orada
`fallthrough` görmediği sürece, o dalın kendi başına bittiğinden emin olabiliyor. Yazılmayan
şey burada bir eksiklik değil, bir **garanti** — geçerli olmayan tek yorum kalıyor: dal
kendiliğinden kapanıyor, aksi yazılmadıkça.

`fallthrough`ın taşıdığı bir kısıtlama daha var: yalnız bir sonraki `case`e düşebiliyor,
kendi koşulunu **denetlemeden**. İkinci `switch`te `case 3`ün kendi koşulu (`3` değerine eşit
olmak) hiç sınanmıyor — `fallthrough` görüldüğü an, sıradaki dal koşulsuz çalıştırılıyor. Bu,
`fallthrough`ı bir `case`in **koşulunu tekrarlamak** ile karıştırmamak gerektiğini gösteriyor:
`fallthrough`, "bir sonraki dalın koşulunu da sına" değil, "bir sonraki dalın **gövdesini de**
çalıştır" demek. Yazma anında ayırt edilmesi gereken bir ayrıntı — `fallthrough`ın sıradaki
dalı sınamadan çalıştırdığını bilmeyen biri, düşen dalın koşulunun da doğrulandığını
sanabilir.

`fallthrough`ın kendi de bir kısıtla geliyor: yalnız bir `case` gövdesinin **son** satırı
olabiliyor. Bir dalın ortasına yazılsaydı derleyici bunu reddederdi — düşme, dalın geri kalanı
çalıştıktan sonra gelen tek bir geçiş, dalın içine serpiştirilebilecek bir dallanma aracı
değil. Bu kısıtlama, `fallthrough`ı sıradan bir `goto`dan ayırıyor: yalnız **bitişik** bir
sonraki dala geçebiliyor ve yalnız bir dalın en sonunda görünebiliyor.

Bu asimetri, kursun ilk dersinde ölçülen "reddetmek daha az yazmak demek değildir"
gözlemine benziyor ama ondan farklı bir yerde duruyor. Orada reddedilen bir kalıbın karşılığı
daha uzun bir yazımdı; burada hiçbir şey reddedilmiyor, yalnız **varsayılan olmayan** yol daha
uzun yazılıyor. Varsayılan davranış hep en kısa yazımı alıyor, varsayılan dışı davranış hep bir
sözcük daha fazla istiyor.

Beş biçimin tamamına birden bakıldığında ortak bir örüntü çıkıyor: Go'nun denetim akışında
yazılmayan her parça, **en sık istenen** davranışın karşılığı oluyor. Bir döngünün sonsuza
kadar sürmesi ender istenir, bu yüzden koşulsuz `for` özel bir sözdizim gerektirmiyor —
yalnız koşulun yokluğu yeterli. Bir `switch` dalının bir sonrakine düşmesi ender istenir, bu
yüzden düşmeme varsayılan; düşmek istendiğinde bu istisnai isteğin kendisi işaretlenmek
zorunda. Bir `if` bloğundan sonra kodun devam etmesi sık istenir, bu yüzden `else` yazmadan da
devam ediyor. Yazılmayan parça hep **çoğunluğun** yolu; yazılması gereken parça hep
**azınlığın** istisnası.

Beş biçimin tamamı, tek bir ölçüm programında yan yana koşturulabiliyor olması da kayda değer:
hiçbiri diğerini etkilemiyor; hiçbiri özel bir kurulum istemiyor. Bu, denetim akışının Go'da
birbirinden bağımsız, küçük kurallar kümesi olarak tasarlandığını gösteriyor — bir biçimi anlamak
için ötekileri bilmek gerekmiyor, her biri kendi başına, tek bir yazılmamışlığa tek bir yanıt
veriyor.

#### Özet

- `for`ın üç bölümü de isteğe bağlı; yazılmayan koşul örtük `true`, yazılmayan ilklendirme ve
  sonlandırma yalnız atlanıyor.
- İfadesiz bir `switch`, örtük `true` ile karşılaştırılır ve her `case` kendi başına bir
  mantıksal ifadeye dönüşür.
- `if` bloğu bir `return` ile bittiğinde, `else` yazılmadan da kalan kod bir `else` bloğu gibi
  çalışır.
- Bir `switch` dalının sonu, yazılmayan bir `break`e gerek kalmadan kendiliğinden kapanır;
  beş biçimin beşi de tanımlı bir karşılıkla doluyor.
- Varsayılanın tersini (düşmeyi) istemek bedelsiz değil: `fallthrough` sözcüğü açıkça
  yazılmak zorunda.

#### Sonraki Adım

Bu derste ölçülen "else'siz erken dönüş" biçimi, bir işlevin **erken** bittiği durumları
gösterdi ama işlevin dönüş değerinin kendisi hiç sorgulanmadı — `siniflandir` her zaman tek
bir dizgi döndürdü ve o dizgiyi hiç kimse yazılmadan bırakmadı. Sıradaki ders bunu soruyor: bir
işlev birden fazla değer döndürdüğünde bunlardan biri yazılmazsa ne oluyor ve adlandırılmış
bir dönüş hiç dokunulmadan bırakıldığında hangi değeri taşıyor?

### Fonksiyonlar

Source: https://academia.sh/tr/kurslar/go-temelleri/fonksiyonlar

Adlandırılmış bir dönüş değerine hiç dokunulmadığında tipinin sıfır değerini taşıyor; bir dönüş değerinin hiç yazılmaması derleme zamanında reddediliyor, ama çağıranın döneni hiç yakalamadan yok sayması hiçbir şey demiyor — Go'da hata bir dönüş değeri olduğu için bu açıkta kalan davranış kursun en önemli örneği. Sınırlayıcı ölçüm: dönüş değeri değişkene alınıp kullanılmadığında bu açıklık kapanıyor ve kullanılmayan değişken reddi devreye giriyor.

Önceki ders "else'siz erken dönüş"ü ölçtü ama işlevin döndürdüğü değerin kendisine hiç
bakmadı. Bu ders o değere bakıyor: bir işlev birden fazla değer döndürdüğünde bunlardan biri
yazılmazsa ne oluyor, adlandırılmış bir dönüş hiç dokunulmadan bırakıldığında hangi değeri
taşıyor ve çağıran taraf dönen değeri hiç yakalamazsa dil buna ne diyor?

Programlama Temelleri kursu (M01/K02) fonksiyon tanımını Python'la kurdu; orada bir işlev tek
bir değer döndürüyordu ya da hiç döndürmüyordu, birden fazla değer bir demet olarak paketlenip
tek bir dönüş yuvasından geçiyordu. **Bu kavram burada tekrar kurulmaz** — farkı tek cümleyle
yazmak yeter: Go'da çoklu dönüş, imzanın **kendisinin** parçasıdır; bir demete paketlenmez,
her değer kendi tipiyle ayrı ayrı yazılır. Bu ders, bu ayrı yuvaların yazılmadığında ne
taşıdığını ölçüyor.

#### Çoklu Dönüş Değeri ve Adlandırılmış Dönüşün Sıfırı

Go'da bir işlev birden fazla değer döndürebiliyor ve bu değerler **adlandırılabiliyor** —
imzada yazılan adlar, işlevin gövdesinde sıradan birer değişken gibi kullanılabiliyor. `bol`
işlevi bunu gösteriyor: `sonuc` ve `hata`, `func` satırında adlandırılmış, gövdede hiç
`return sonuc, hata` yazılmadan, çıplak bir `return` ile dönüyor.

Sıfır değerler dersinde ölçülen `adliDonus` işlevi bu davranışı zaten bir satırda göstermişti:
`func adliDonus() (sonuc int, hata error) { return }`, hiçbir gövde çalıştırmadan iki sıfır
değeri dönüyordu. Bu ders aynı düzeneği, gövdesi gerçekten çalışan, koşula göre dallanan bir
işlevde ölçüyor — `bol`, iki farklı yoldan geçebiliyor ve her yolda adlandırılmış dönüşün ne
kadarına dokunulduğu değişiyor.

```go
// fonksiyon.go — coklu donus degeri ve adlandirilmis donusun yazilmadiginda sifir donmesi
package main

import "fmt"

func bol(a, b int) (sonuc int, hata error) {
	if b == 0 {
		hata = fmt.Errorf("sifira bolme")
		return
	}
	sonuc = a / b
	return
}

func main() {
	s1, h1 := bol(10, 2)
	fmt.Printf("%-24s sonuc=%v hata=%v\n", "basarili bolme", s1, h1)

	s2, h2 := bol(10, 0)
	fmt.Printf("%-24s sonuc=%v hata=%v\n", "sifira bolme", s2, h2)

	bol(10, 0)
	fmt.Printf("%-24s %s\n", "yok sayilan cagri", "panik yok, program devam ediyor")
}
```

```
basarili bolme           sonuc=5 hata=<nil>
sifira bolme             sonuc=0 hata=sifira bolme
yok sayilan cagri        panik yok, program devam ediyor
```

**DT20.** `b == 0` dalında yalnız `hata`ya değer atanıyor; `sonuc`a hiç dokunulmuyor. Yazdırılan
`sonuc=0`, kaynakta hiçbir yerde `sonuc = 0` yazılmadığı hâlde çıkıyor — `int`in sıfır değeri,
Sıfır Değerler dersinde ölçülen çerçevenin doğrudan bir uzantısı. Adlandırılmış bir dönüş değeri,
işlev başladığı anda kendi tipinin sıfır değeriyle **önceden ilklendirilmiş** bir yerel değişken
gibi davranıyor; gövde ona hiç dokunmazsa, döndürdüğü şey o ilk değerdir.

Bu ilklendirme, işlev çağrıldığı **her seferinde** tazeleniyor. `bol(10, 2)` ve `bol(10, 0)`
aynı işlevin iki ayrı çağrısı ve ikisinde de `sonuc` sıfırdan başlıyor; birinci çağrıda `5`e
atanıyor, ikincisinde hiç atanmadığı için sıfırda kalıyor. Bir önceki çağrının `sonuc`u,
sıradaki çağrıyı hiçbir şekilde etkilemiyor — her çağrı, adlandırılmış dönüşlerini kendi sıfır
değerleriyle taze başlatıyor.

İkinci satır bunu daha da netleştiriyor: `hata` atandığı için `hata=sifira bolme` görünüyor,
ama `sonuc` hâlâ `0`. İki değer aynı `return` ifadesiyle dönüyor, ama yalnız biri kaynakta
elle yazıldı. Adlandırılmış dönüş, çoklu dönüş değerinin **bazılarını** yazıp geri kalanını
sıfır değerine bırakmanın yolunu açıyor — imzada iki ad birden görünse de, gövdenin ikisine de
dokunması zorunlu değil.

Çıplak `return` (`return sonuc, hata` yerine yalnız `return`) burada bir kısaltmadan fazlası.
Adlandırılmamış bir çoklu dönüşte her `return` ifadesi, dönecek değerleri açıkça sıralamak
zorundadır; adlandırıldığında, kaynakta iki kez tekrar edecek adları yazmadan, işlevin o anki
durumunu olduğu gibi dönmek mümkün oluyor. Bu, `sonuc` ve `hata`nın işlev boyunca **paket
düzeyi değil yerel** değişkenler gibi davrandığını gösteriyor — imzada bildirilmiş olsalar
da, gövdenin herhangi bir noktasında okunabilir ve değiştirilebilirler.

Adlandırmanın bir de belgeleme yanı var. `func bol(a, b int) (int, error)` yazılsaydı, imza
yalnız iki tipi bildirirdi; hangi değerin sonucu, hangisinin hatayı taşıdığını anlamak için
gövdeyi okumak gerekirdi. `sonuc` ve `hata` adlarının imzada yazılması, bu bilgiyi gövdeye
inmeden veriyor — imza kendi başına, hangi dönüş yuvasının neyi taşıdığını söylüyor. Bu
yüzden adlandırılmış dönüşler, kısa bildirimin tam tersi bir yönde çalışıyor: kısa bildirim
tip adını **kaldırıyordu**, adlandırılmış dönüş bir **anlam adı ekliyor**.

Bu belgeleme kazancı, adlandırmanın **zorunlu olmamasıyla** birlikte okunmalı. `func bol(a, b
int) (int, error)` da geçerli bir imzadır ve tamamen aynı şekilde iki değer döndürür;
adlandırma yalnız okunabilirlik için tercih ediliyor, davranışı değiştirmiyor. Bir işlevin
adlandırılmış dönüşe ihtiyacı olup olmadığı, kaç değer döndürdüğüne değil, o değerlerin
anlamının imzadan ne kadar açık okunduğuna bağlı.

#### Yazılmayan Dönüş Değeri Reddediliyor, Kullanılmayan Değil

İlk derste ölçülen dört retten biri, dönüş değerinin **yazılmamasıydı**: `func F() int { }`
gövdesi hiçbir değer üretmeden bitiyordu ve derleyici bunu reddediyordu. Bu ders o reddin tam
tersini ölçüyor: dönüş değerini **çağıran tarafın** hiç kullanmaması aynı şekilde reddediliyor
mu? Sezgisel olarak "hayır" denebilir — bir işlevi çağırıp sonucunu kullanmamak, birçok
bağlamda tamamen sıradan bir işlem (bir günlük yazma çağrısının dönüşü genelde önemsizdir). Bu
ders sezgiyi değil, gerçek davranışı ölçüyor.

Üçüncü satır bu soruyu koşturarak yanıtlıyor. `bol(10, 0)` çağrısı, hiçbir değere atanmadan, tek
başına bir ifade olarak yazılıyor — iki dönüş değeri de (sonuç ve hata) sessizce atılıyor. Program
panik yapmıyor, derleyici hiçbir şey söylemiyor; çalıştırma normal şekilde devam ediyor.

**DT21.** Bu satırda hiçbir değişken tanımlanmıyor — yalnız bir çağrı yapılıyor. İlk derste
ölçülen "kullanılmayan değişken" reddi, tanımlanan bir adın hiç okunmamasına uygulanıyordu;
burada tanımlanan hiçbir ad yok, o yüzden o kural devreye hiç girmiyor. Dönen iki değer,
hiçbir yere yazılmadan, çağrının kendisiyle birlikte kayboluyor.

Bu, ilk derste ölçülen dört retle bir arada okunduğunda çarpıcı bir asimetri ortaya çıkarıyor.
`func F() int { }` gibi bir gövde, dönüş değerini **yazmadığı** için reddediliyordu — derleyici
imzanın verdiği sözü tutmayan bir gövdeyi kabul etmiyordu. Ama `bol(10, 0)` çağrısı, dönüş
değerini fazlasıyla **üretiyor** — işlev sözünü tutuyor, iki değer de hesaplanıyor — yalnız
çağıran taraf bu üretilmiş değerleri hiç talep etmiyor. Derleyicinin ilgisi üretimin
kendisiyle sınırlı; üretilen bir değerin sonradan ne olduğu, işlevin sınırları dışında kalan
bir mesele.

Bu, kursun **açıkta** sınıfının şimdiye kadarki en önemli örneği, çünkü Go'da hata bir
sıradan **dönüş değeridir** — ayrı bir mekanizma, bir istisna türü ya da zorunlu bir
yakalama yapısı yok. Bir işlev bir `error` döndürüyorsa, bu değeri okuyup okumamak tamamen
çağıran tarafın kararı. Dil, dönen bir hatanın kontrol edilmesini **hiçbir şekilde**
zorunlu kılmıyor; `bol(10, 0)`ın döndürdüğü hata, tıpkı bir günlüğe hiç yazılmamış bir olay
gibi sessizce yok oluyor.

Bu davranışın kaynakta hiçbir izi de kalmıyor. Üçüncü satırın çıktısı ("panik yok, program
devam ediyor") bunu doğruluyor: `sifira bolme` hatası gerçekten üretiliyor, ama hiçbir yere
yazılmadığı için hiçbir etkisi olmuyor — ne bir panik, ne bir günlük satırı, ne bir çıkış
kodu değişikliği. Kaynağı okuyan biri, `bol(10, 0)` satırına bakarak bu çağrının bir hata
üretip üretmediğini bile göremez; bunu anlamak için işlevin **tanımına** bakmak gerekiyor.
Bu, açıkta kalan davranışın ortak özelliği: sonucu görmek için çağrı satırı yetmiyor,
tanımın kendisi okunmak zorunda.

#### Sınırlayıcı Ölçüm: Yok Sayılan Her Zaman Açıkta Kalmaz

Bu açıklığın bir sınırı var. Dönen değerleri **hiç yakalamadan** yok saymak serbestken, onları
bir değişkene **alıp** sonra kullanmamak serbest değil. Bunu ölçmek için ilk dersteki
derleyici çağrısına dönüyoruz ve `bol`u iki farklı kaynak parçasında çağırıyoruz — biri
sonuçları hiç yakalamıyor, öbürü yakalayıp yalnızca birini kullanıyor.

```go
// derleyici.go — derleyiciyi program icinden cagiran yardimci
package main

import (
	"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
}

func sinif(ok bool) string {
	if ok {
		return "derlendi"
	}
	return "derlenmedi"
}
```

```go
// sinama.go — tumuyle yok saymak ile degiskene alip yari kullanmamak ayni sey degil
package main

import "fmt"

func main() {
	tumuyleYokSayilan := `package sinama
import "fmt"
func bol(a, b int) (sonuc int, hata error) {
	if b == 0 {
		hata = fmt.Errorf("sifira bolme")
		return
	}
	sonuc = a / b
	return
}
func F() { bol(10, 2) }`

	yariYokSayilan := `package sinama
import "fmt"
func bol(a, b int) (sonuc int, hata error) {
	if b == 0 {
		hata = fmt.Errorf("sifira bolme")
		return
	}
	sonuc = a / b
	return
}
func F() { s, h := bol(10, 2); _ = s }`

	fmt.Printf("%-34s %s\n", "tumuyle yok sayilan", sinif(derlenirMi(tumuyleYokSayilan)))
	fmt.Printf("%-34s %s\n", "degiskene alinip yari kullanilan", sinif(derlenirMi(yariYokSayilan)))
}
```

```
tumuyle yok sayilan                derlendi
degiskene alinip yari kullanilan   derlenmedi
```

**DT22.** İki kaynak da aynı `bol` işlevini çağırıyor ve aynı sonucu (hata) yok sayıyor;
aradaki tek fark, ikinci kaynağın dönen değerleri `s` ve `h` adlı değişkenlere **alması**.
Birinci kaynakta hiçbir ad tanımlanmadığı için kullanılmayan değişken reddi hiç devreye
girmiyor. İkinci kaynakta `h` adı tanımlanıyor ve hiç okunmuyor — `_ = s` yalnız `s`yi
kurtarıyor, `h` kullanılmayan değişken kalıyor ve derleme reddediliyor.

Bu, "açıkta" sınıfının sınırını netleştiriyor. Bir dönüş değerini yakalamamak, dilin hiçbir
zaman göremeyeceği bir karardır — çağrı satırında hiçbir ad yok, denetlenecek bir şey yok.
Ama bir kez bir ada bağlandığında, o ad artık ilk derste ölçülen kuralın kapsamına giriyor:
yazılan her ad ya kullanılmalı ya da kaynakta hiç yazılmamalı. Hatanın kendisi "açıkta"
kalmaya devam ediyor — dil hâlâ onu okumanızı zorunlu kılmıyor — ama onu bir **ada** bağlayıp
sonra unutmak, farklı bir kurala takılıyor.

Bu ayrım, Go'da hata denetiminin neden çoğunlukla `sonuc, hata := bol(...)` biçiminde
yazıldığını da açıklıyor. `hata` bir değişkene alındığı anda, onu hiç okumadan bırakmak
derlemeyi durduruyor — `if hata != nil` yazmak bir stil tercihi değil, `hata` adını
**kullanılmış** saymanın en doğal yolu. Dönüş değerini yakalamadan tamamen görmezden gelmek
hâlâ mümkün, ama bir kez yakalandığında görmezden gelmek artık mümkün değil.

Bu, kaynağı okuyan biri için de bir işaret taşıyor: bir çağrının sonucu hiç yakalanmadıysa,
o çağrının hatasının hiç denetlenmediği kesin — ama bir sonuç yakalandıysa, o sonucun en
azından **derlenebilir olacak kadar** kullanıldığı da kesin. Kullanım, denetim anlamına
gelmiyor — `_ = h` yazıp `h`yi hiç okumadan da "kullanılmış" sayılabilirdi — ama en azından
adın kaynakta bir amaçla durduğunu gösteriyor. Bu, ilk derste ölçülen boş tanımlayıcının
üçüncü bir kullanımı: `_ = h` yazmak, "bu hatayı biliyorum ama bilerek denetlemiyorum" demenin
yazılı biçimi. Kullanılmayan değişken reddi, bir hatayı **denetlemeye** zorlamıyor — yalnız
onu **isimlendirip unutmaya** izin vermiyor.

Aradaki fark küçük görünse de pratikte önemli: bir kod tabanında hangi hataların hiç
yakalanmadığını bulmak için çağrı satırlarını taramak gerekir (derleyici burada hiçbir iz
bırakmaz), ama hangi yakalanmış hataların denetlenmediğini bulmak biraz daha kolaydır — en
azından `_ = hata` gibi bilinçli bir görmezden gelme izini aramak mümkündür. Derleyici birinci
grubu hiç göremiyor, ikinci grubu da yalnız "kullanılmadı mı" sorusuyla görüyor, "denetlendi
mi" sorusuyla değil.

Bu dersin üç ölçümü — adlandırılmış dönüşün sıfırı, yazılmayan dönüşün reddi, kullanılmayan
dönüşün açıklığı — kursun üç sınıfının üçünü de tek bir işlev üzerinde gösteriyor. `bol`un
kendisi hiç değişmedi; değişen, onu **nasıl çağırdığımız**. Aynı işlev, adlandırılmış bir
dönüşle çağrıldığında tanımlı bir sıfır değer taşıyor, imzasız bırakıldığında derlemeyi
durduruyor, çağrıda yakalanmadan bırakıldığında sessizce kayboluyor. Yazılmayanın karşılığı,
bu derste ilk kez işlevin **kendisine** değil, işlevin **çağrılma biçimine** bağlı hâle
geliyor.

Üç ölçümü bir arada okuduğumuzda, Go'nun fonksiyon tasarımının aynı ilkeyi üç kere tekrarladığı
görülüyor: **üretim** zorunlu, **tüketim** değil. Bir işlev, imzasının söz verdiği her değeri
üretmek zorunda — ya elle yazarak ya da adlandırılmış dönüşün sıfırına güvenerek. Ama o
üretilmiş değeri çağıran tarafın **alması**, dilin hiçbir zaman zorunlu kıldığı bir şey değil.
Yalnız bir kez alındığında, artık o alınan şeyin de kaynakta bir amaca hizmet etmesi gerekiyor.

#### Özet

- Adlandırılmış bir dönüş değerine gövde hiç dokunmazsa, o değer kendi tipinin sıfır değerini
  taşır — `sonuc=0`, kaynakta hiçbir yerde `sonuc = 0` yazılmadan çıkıyor.
- Bir dönüş değerinin hiç yazılmaması (`func F() int { }`) derleme zamanında reddediliyor.
- Çağıranın dönen değerleri hiç yakalamadan yok sayması hiçbir şekilde reddedilmiyor — Go'da
  hata bir dönüş değeri olduğu için bu, kursun en önemli açıkta örneği.
- Dönen değerler bir değişkene alındığında durum değişiyor: kullanılmayan değişken reddi
  devreye giriyor ve yarım kullanım artık derlenmiyor.
- Hatanın kendisi hep açıkta kalıyor; onu bir ada bağlayıp sonra unutmak farklı, ayrı bir
  kurala takılıyor.
- Fonksiyon tasarımının ilkesi tek: bir işlev söz verdiği her değeri üretmek zorunda, ama
  çağıran tarafın o değeri alması dilin hiçbir zaman zorunlu kıldığı bir şey değil.

#### Sonraki Adım

Bu derste `bol` işlevi hep iki sabit argümanla çağrıldı ve dönüş tarafındaki yazılmamışlıklar
ölçüldü — argüman tarafında hiçbir belirsizlik yoktu, imza kaç argüman istediğini kesin olarak
söylüyordu. Ama bazı işlevler kaç argüman alacağını önceden bilmiyor — çağıran taraf sıfır, bir
ya da yirmi değer geçebiliyor. Sıradaki ders, bu konunun son dersi, bu belirsizliği ölçüyor:
değişken sayıda argüman alan bir parametre, hiç argüman verilmediğinde ne oluyor ve bu
dersin kurduğu "yakalanan değerin adı" ile bir dilimin kendisi arasında nasıl bir ilişki var?

### Değişken Sayıda Argüman

Source: https://academia.sh/tr/kurslar/go-temelleri/degisken-sayida-arguman

Hiç argüman verilmeyen bir değişken sayıda argüman parametresi nil bir dilim oluyor, tek tek yazılan argümanlar taze bir dilim üretiyor, mevcut bir dilimin ... ile açılması ise kopyalamadan aynı alt diziyi paylaşıyor — çağrılan tarafın yaptığı değişiklik çağırana yansıyor. Sınırlayıcı ölçüm: nil dilim ile boş dilim uzunlukça aynı ama == nil'de ayrışıyor ve bu fark JSON karşılığında görünür oluyor.

Önceki ders, bir dönüş değerinin bir ada bağlandığında kullanılmak zorunda olduğunu ölçtü. Bu
ders aynı soruyu argüman tarafına taşıyor: bir işlev kaç argüman alacağını önceden
bilmediğinde, o parametre hiç argüman verilmediğinde ne taşıyor ve mevcut bir dilim bu
parametreye **açılarak** geçirildiğinde aradaki ilişki nedir?

Programlama Temelleri kursu (M01/K02) değerle ve referansla aktarımı Python'la kurdu; orada
bir listeyi bir işleve geçirmek, listenin kendisini paylaşmak anlamına geliyordu. **Bu kavram
burada tekrar kurulmaz** — farkı tek cümleyle yazmak yeter: Go'da her şey değerle aktarılıyor,
dilimler de dahil; bu dersin ölçtüğü, "değerle aktarım" ile "paylaşılan bir alt diziye işaret
eden bir değerin aktarımı" arasındaki farkın değişken sayıda argümanlarda nasıl göründüğü.

#### Üç Çağrı Biçimi

Go'da bir parametrenin önüne `...` konursa, o parametre **değişken sayıda argüman** kabul
eder ve işlev gövdesinde bir dilim gibi davranır. Çağıran taraf üç ayrı biçimde çağırabilir:
hiç argüman vermeden, argümanları tek tek sıralayarak, ya da elinde zaten duran bir dilimi
`...` ile açarak. Üçü de sözdizim olarak geçerli, ama imzanın arkasında üçü de aynı şeyi
üretmiyor.

Bu kursun kendisi, değişken sayıda argümanı en baştan beri kullanıyor: her `fmt.Printf` ve
`fmt.Println` çağrısı, standart kitaplığın değişken sayıda argüman alan işlevleri. Bir çağrıda
tek bir değer, başka bir çağrıda beş değer geçirebilmemiz, tam olarak bu dersin ölçtüğü
düzenek sayesinde. Bu ders, o düzeneğin kendi işlevlerimizi yazarken nasıl davrandığını
soruyor.

İki yardımcı işlev bu parametrenin davranışını iki yönden ölçüyor: `ozellikleri`, uzunluğunu
ve `nil` olup olmadığını döndürüyor; `ilkiSifirla`, ilk elemanını değiştirip bu değişikliğin
çağırana yansıyıp yansımadığını sınamamıza yarıyor.

```go
// yardimci.go — degisken sayida arguman alan iki yardimci
package main

func ozellikleri(sayilar ...int) (uzunluk int, nilMi bool) {
	return len(sayilar), sayilar == nil
}

func ilkiSifirla(sayilar ...int) {
	if len(sayilar) > 0 {
		sayilar[0] = 0
	}
}
```

**DT23.** `ozellikleri`nin dönüş imzası önceki dersin adlandırılmış dönüşünü kullanıyor;
`sayilar == nil` karşılaştırması, bir dilimin `nil` olup olmadığını sınamanın standart yolu —
Sıfır Değerler dersinde ölçülen aynı karşılaştırma burada bir parametre üzerinde koşuyor.
`ilkiSifirla` de aynı dersten bir tekniği ödünç alıyor: `len(sayilar) > 0` denetimi, `nil`
dilime indeksleme yaparak panikletmemek için — `nil` bir dilimin sıfırıncı elemanına erişmek,
tıpkı boş bir dilimde olduğu gibi, dizinin sınırları dışına taşar.

Üç çağrı biçimini yan yana koşturuyoruz: hiç argüman verilmeden, argümanlar tek tek yazılarak
ve mevcut bir dilim `...` ile açılarak.

```go
// degisken.go — hic arguman verilmedigindeki dilim, dilimin acilmasi, acilan dilimin kopyalanmamasi
package main

import "fmt"

func main() {
	u1, n1 := ozellikleri()
	fmt.Printf("%-28s uzunluk=%d nil=%v\n", "hic arguman verilmedi", u1, n1)

	u2, n2 := ozellikleri(1, 2, 3)
	fmt.Printf("%-28s uzunluk=%d nil=%v\n", "uc arguman tek tek yazildi", u2, n2)

	kaynak := []int{10, 20, 30}
	u3, n3 := ozellikleri(kaynak...)
	fmt.Printf("%-28s uzunluk=%d nil=%v\n", "mevcut dilim acildi (...)", u3, n3)

	ilkiSifirla(1, 2, 3)
	fmt.Printf("%-28s %s\n", "tek tek yazilan degisti mi", "hicbir sey gozlenemez, cagirana ait dilim yok")

	ilkiSifirla(kaynak...)
	fmt.Printf("%-28s kaynak=%v\n", "acilan dilim degisti mi", kaynak)
}
```

```
hic arguman verilmedi        uzunluk=0 nil=true
uc arguman tek tek yazildi   uzunluk=3 nil=false
mevcut dilim acildi (...)    uzunluk=3 nil=false
tek tek yazilan degisti mi   hicbir sey gozlenemez, cagirana ait dilim yok
acilan dilim degisti mi      kaynak=[0 20 30]
```

**DT24.** İlk üç satır, aynı parametrenin üç farklı çağrı biçiminde ne taşıdığını gösteriyor.
Hiç argüman verilmediğinde (`ozellikleri()`), parametre `nil` bir dilim — uzunluğu `0` ve
kimliği boş. Argümanlar tek tek yazıldığında (`ozellikleri(1, 2, 3)`), derleyici bu üç değeri
tutacak **taze bir dilim** oluşturuyor; bu dilim `nil` değil, çünkü bir yerde gerçekten
ayrılmış. Mevcut bir dilim `...` ile açıldığında (`ozellikleri(kaynak...)`), uzunluk ve
`nil`lik ikinci satırla aynı çıkıyor — ama bu satırın asıl farkı, dördüncü ve beşinci
satırlarda ortaya çıkıyor.

Birinci satırdaki `nil` sonucu, ilk derste kurulan kalıbı doğruluyor: hiç argüman verilmemesi
bir hata değil, tanımlı bir sıfır değer. İkinci satır ise ilk kez, bir değişkenin değil, bir
**çağrının kendisinin** bir dilim ürettiğini gösteriyor — `1, 2, 3` yazmak, kaynakta hiçbir
dilim değişmezi (`[]int{1, 2, 3}`) görünmese de, çağrı anında bir dilim inşa ediyor.

İlk üç satırı yalnız `uzunluk` ve `nil` sütunlarına bakarak okursak, ikinci ve üçüncü satır
birbirinden ayırt edilemez: ikisi de `uzunluk=3 nil=false`. Bu, ölçümün bir sınırını gösteriyor
— `ozellikleri` işlevinin kendisi, kendisine geçirilen dilimin **taze** mi yoksa **paylaşılan**
mı olduğunu bilmiyor, çünkü bu bilgi dilim başlığının hiçbir alanında (uzunluk, kapasite,
işaretçi) doğrudan görünmüyor. Bu ayrımı görmek için başka bir ölçüme, `ilkiSifirla`ya
ihtiyaç var.

#### Açılan Dilim Kopyalanmıyor

Bu belirsizlik, "tanımlı" sınıfının bir başka sınırını daha gösteriyor. Sekiz tanımlı satırın
her biri belirli bir soruya kesin bir yanıt veriyordu; burada soru başka — bir işlevin kendi
parametresine bakarak, o parametrenin çağıranla ne kadar bağlantılı olduğunu **anlayabilmesi**
mi soruluyor. Yanıt hayır: dilim başlığı bu bilgiyi taşımıyor, yalnız davranışı gözlemleyerek
(değiştirip sonucu izleyerek) ortaya çıkarılabiliyor.

Dördüncü ve beşinci satırlar aynı işlevi (`ilkiSifirla`) iki farklı çağrı biçimiyle
çalıştırıyor. `ilkiSifirla(1, 2, 3)` çağrısında değiştirilen dilim, yalnız o çağrı için
oluşturulmuş taze bir dilim — çağıran tarafta bu dilime karşılık gelen hiçbir değişken yok,
bu yüzden "değişti mi" sorusunun bile bir anlamı yok. `ilkiSifirla(kaynak...)` çağrısında ise
durum farklı: `kaynak`, çağrıdan önce `[10 20 30]`, çağrıdan sonra `[0 20 30]`. İşlevin içinde
`sayilar[0] = 0` yazmak, çağıranın kendi `kaynak` değişkenini değiştirdi.

**DT25.** Bu, `...` ile açılan bir dilimin **kopyalanmadığını** gösteriyor. Go, `f(dilim...)`
çağrısında yeni bir alt dizi ayırmıyor; `dilim`in kendi başlığını (uzunluk, kapasite, alt
diziye işaretçi) doğrudan parametreye geçiriyor. Parametre ile `kaynak`, aynı alt diziyi
**paylaşıyor**; parametre üzerinden yapılan bir eleman değişikliği, aynı alt diziyi okuyan
`kaynak` üzerinden de görünür oluyor.

Bu, "değerle aktarım" kuralını (Programlama Temelleri kursunda kavram olarak kuruldu, burada
tekrar kurulmuyor) çelişkili gibi gösterebilir — sanki dilim "referansla" geçiyormuş gibi
görünüyor. Ama aktarılan şey yine bir **değerdir**: dilim başlığının kendisi kopyalanıyor,
yalnız o başlığın işaret ettiği alt dizi kopyalanmıyor. `ilkiSifirla(1, 2, 3)` ile
`ilkiSifirla(kaynak...)` arasındaki fark, aktarım kuralında değil, **hangi alt dizinin**
paylaşıldığında: birincisinde parametre kendi taze alt dizisine işaret ediyor ve o alt diziyi
kimse başka bir yerden görmüyor; ikincisinde parametre `kaynak`ın **zaten var olan** alt
dizisine işaret ediyor ve o alt diziyi `kaynak` da görmeye devam ediyor.

Bu ayrım, tek tek yazılan argümanların **her zaman** güvenli olduğu, açılan bir dilimin ise
**bazen** paylaşım riski taşıdığı anlamına geliyor. `topla(1, 2, 3)` gibi bir çağrıda, işlevin
argümanları değiştirmesi hiçbir zaman çağıranı etkilemez, çünkü paylaşılacak bir çağıran-taraf
dilimi yok. `topla(dilim...)` gibi bir çağrıda ise işlevin kendi parametresine yazması,
`dilim`in kendisini de değiştirebilir. Bu, bir işlevi yazarken sorulması gereken bir soruyu
ortaya çıkarıyor: değişken sayıda argüman alan bir işlev, kendi parametresine **yazıyorsa**,
bu yazmanın çağıranı da etkileyebileceği kaynak kodunda belirtilmeli — imza bu riski kendisi
göstermiyor, çünkü `func ilkiSifirla(sayilar ...int)` imzası, `ilkiSifirla(1, 2, 3)` ile
`ilkiSifirla(kaynak...)` çağrılarının ikisi için de aynı.

Bu, standart kitaplığın değişken sayıda argüman alan işlevlerinin neden neredeyse hiçbirinin
kendi parametresine **yazmadığını** açıklıyor: `fmt.Println`, argümanlarını yalnız okuyor, hiç
değiştirmiyor. Bir işlev yazarken kendi değişken sayıda argüman parametresine yazma ihtiyacı
duyuluyorsa, bu ders bir uyarı taşıyor — o yazma, çağıranın `...` ile açtığı bir dilime
sessizce sızabiliyor.

#### Sınırlayıcı Ölçüm: Nil Dilim İle Boş Dilim Aynı Değildir

İlk satırda `nil` bir dilim, ikinci ve üçüncü satırlarda `nil` olmayan dilimler gördük.
Ama `nil` olmayan bir dilim, boş da olabilir. `ozellikleri()` ile `ozellikleri([]int{}...)`
karşılaştırıldığında bu fark netleşiyor: birincisinde hiçbir argüman yok, ikincisinde bir
argüman var — o argüman, `[]int{}` ile kurulmuş, sıfır elemanlı ama gerçekten var olan bir
dilim.

```go
// sinirlayici.go — nil dilim ile bos dilim ayni degildir
package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	uNil, nNil := ozellikleri()
	uBos, nBos := ozellikleri([]int{}...)

	fmt.Printf("%-28s uzunluk=%d nil=%v\n", "hic arguman (nil dilim)", uNil, nNil)
	fmt.Printf("%-28s uzunluk=%d nil=%v\n", "bos dilim acildi ([]int{}...)", uBos, nBos)

	var nilDilim []int
	bosDilim := []int{}

	bNil, _ := json.Marshal(nilDilim)
	bBos, _ := json.Marshal(bosDilim)
	fmt.Printf("%-28s json=%s\n", "nil dilimin json karsiligi", bNil)
	fmt.Printf("%-28s json=%s\n", "bos dilimin json karsiligi", bBos)
}
```

```
hic arguman (nil dilim)      uzunluk=0 nil=true
bos dilim acildi ([]int{}...) uzunluk=0 nil=false
nil dilimin json karsiligi   json=null
bos dilimin json karsiligi   json=[]
```

İlk iki satır, `len`in ayırt edemediği bir farkı gösteriyor: ikisinin de uzunluğu `0`, ama
biri `nil`, öbürü değil. `[]int{}`, boş de olsa, bir yere **ayrılmış** bir dilim; hiçbir
elemanı yok ama kimliği var. `nil` dilim ise hiçbir yere ayrılmamış, kimliği de yok. `len` bu
ikisini ayırt etmiyor çünkü uzunluk, ayrılmış olup olmamayı değil, kaç eleman tutulduğunu
sayıyor.

Fark somut bir yerde görünüyor: `encoding/json` paketinin dönüştürücüsü. `nil` bir dilim
JSON'da `null` olarak yazılıyor, boş ama `nil` olmayan bir dilim ise `[]` olarak yazılıyor.
İkisi de "hiçbir eleman yok" bilgisini taşısa da, dönüştürücü bu iki durumu **ayrı** karşılıklar
olarak ele alıyor — biri "değer yok", öbürü "değer var ama boş". Standart kitaplığın bu
seçimi, `nil` ile boş dilim arasındaki farkı yalnız bir dil kuralı olmaktan çıkarıp, gerçek bir
programın çıktısında görünür bir sonuca dönüştürüyor.

Bu iki karşılık, aynı iki dilimin başka bir bağlamda tamamen aynı davranmasıyla da tezat
oluşturuyor. Bir `for range` döngüsüyle gezildiklerinde, `nil` dilim ile boş dilim arasında
hiçbir fark yok — ikisi de hiç yinelenmeden döngüyü bitiriyor. `append` çağrıldığında da ikisi
aynı davranıyor — ikisine de eleman eklenebiliyor. Fark yalnız **kimlik sorulduğunda**
(`== nil`) ya da **kimliğe duyarlı bir kod** (`encoding/json` gibi) devreye girdiğinde ortaya
çıkıyor. Bu, "tanımlı" sınıfının kendi içinde bir alt ayrımı daha olduğunu gösteriyor: bazı
işlemler bir dilimin kimliğine bakıyor, bazıları yalnız içeriğine.

Bu, kursun sıfır değer ölçümüne bir sınır daha ekliyor. `nil` dilim, Sıfır Değerler dersinde
ölçüldüğü gibi tanımlı bir sıfır değer taşıyor ve `append` gibi işlemlere açık. Ama `nil`
olmak ile "boş olmak" aynı şey değil ve bu fark yalnız `== nil` karşılaştırmasında değil,
dilimi işleyen başka kodlarda (burada bir dönüştürücüde) da yüzeye çıkabiliyor. Bir işlevin
hiç argüman almadığını (`nil`) mi, yoksa boş bir dilimle çağrıldığını (`[]int{}...`) mı
bildiği, çağrılan tarafın davranışını değiştirebiliyor.

Sekiz dersin sonunda, açılış dersinde sorulan soruya geri dönebiliriz: bir dil, yazmadığın şeyin
karşılığını nasıl belirler? Bu konu boyunca bu soru altı farklı yerde soruldu — bir kaynağın
derlenebilmesi için ne gerektiğinde, bir değişkenin tipi çıkarılırken, bir sabit dizisi
doldurulurken, bir denetim yapısının parçası atlanırken, bir dönüş değeri yazılmadığında ve şimdi
bir parametreye hiç argüman verilmediğinde. Yanıt hiçbir zaman tek biçimli değildi: kimi zaman dil
tanımlı bir değer koydu, kimi zaman derleyici kaynağı hiç kabul etmedi, kimi zaman sonuç çalışma
zamanına kaldı. Sıfır Değerler dersinde ölçülen 8/4/3 dağılımı bu üç yanıtın hiçbirinin baskın
olmadığını, üçünün de dilin tasarımının bir parçası olduğunu gösterdi.

Bu derste ölçülen `nil` dilim, o dağılımın **en görünür** üyesi, çünkü bir dilim üzerinde
neredeyse her işlem (`len`, `append`, `for range`) `nil` durumunu sorunsuzca kaldırıyor — bir
programcı `nil` bir dilimle günlerce çalışıp hiç fark etmeyebilir. Bu görünmezlik, tam da bu
dersin gösterdiği gibi, bir sınırla bitiyor: kimlik sorulduğunda ya da kimliğe duyarlı bir kod
devreye girdiğinde, `nil` olmak aniden görünür hale geliyor.

Bu konunun sekiz dersi boyunca ölçüm hep aynı düzeni izledi: iki hâli yan yana koymak, farkı
koşturarak göstermek, sonra o farkın nerede kırıldığını bulmak. Bu son ders de aynı düzeni
taşıyor — üç çağrı biçimi yan yana koşturuldu, aralarındaki fark (paylaşım) ölçüldü ve o
farkın kendisinin bile bir sınırı (nil ile boş dilim ayrımı) bulundu. Sıradaki konu, aynı
düzeni tek bir işlevin parametresinden bütün bir tip ailesine taşıyor.

#### Özet

- Değişken sayıda argüman alan bir parametreye hiç argüman verilmezse, parametre `nil` bir
  dilim olur.
- Argümanlar tek tek yazıldığında derleyici taze bir dilim oluşturur; bu dilim `nil` değildir.
- Mevcut bir dilim `...` ile açıldığında kopyalanmaz — parametre, çağıranın dilimiyle aynı alt
  diziyi paylaşır ve çağrılan tarafın değişikliği çağırana yansır.
- `nil` dilim ile boş dilim `len` açısından aynıdır (`0`) ama `== nil` açısından ayrışır.
- Bu fark yalnız karşılaştırmada kalmıyor: `encoding/json` paketi `nil` dilimi `null`,
  boş dilimi `[]` olarak yazıyor.
- `for range` ve `append` gibi işlemler `nil` ile boş dilimi ayırt etmiyor; fark yalnız kimliğe
  bakan işlemlerde (`== nil`, `encoding/json`) ortaya çıkıyor.

#### Sonraki Adım

Hiç argüman verilmeyen bir parametrenin `nil` bir dilim olması, sıfır değerin bu kursta
gördüğümüz en görünür sonucu. Bu konu burada kapanıyor; sıradaki konu bu tipin kendisine
bakıyor — dilim, sözlük ve yapı yazılmayanı nasıl karşılıyor ve bir değeri bir değişkene
atadığında ortaya çıkan şey bir **kopya** mı, yoksa bu derste gördüğümüz gibi bir
**paylaşım** mı?

## Bileşik Tipler

Dizinin sabit uzunluğu ile dilimin uzunluk-kapasite ikilisi ve büyüme davranışı, dilim işlemlerinde paylaşılan alt yapının kopyalama tuzakları, sözlükte olmayan anahtarın iki yanıt birden vermesi, yapı alanlarının düzeni ve gömme, adres almanın ve değer ile işaretçi alıcı seçiminin sonuçları, dizginin bayt dizisi olmasıyla rune ayrımı.

### Diziler ve Dilimler

Source: https://academia.sh/tr/kurslar/go-temelleri/diziler-ve-dilimler

Aynı atama satırı dizide kopya, dilimde paylaşım üretiyor: dizinin uzunluğu tipin parçasıdır ve atandığında bütün öğeler kopyalanır, dilimin atanması ise yalnızca başlığı kopyalar ve alt diziyi kaynakla paylaşır. Sınırlayıcı ölçüm dilimin de bir değer olduğunu gösteriyor: paylaşım, append çağrısı kapasiteyi aştığında kesiliyor, çünkü kopyalanan başlık artık ayrı bir alt diziyi gösteriyor.

Değişken Sayıda Argüman dersi, hiç argüman verilmeyen bir parametrenin `nil` bir dilim
olduğunu ölçtü — sıfır değerin en görünür sonuçlarından biriydi bu. Bu ders aynı tipin
kendisine döner: dilim, sözlük ve yapı yazılmayanı nasıl karşılıyor ve daha önce hiç
sorulmamış bir soru — bir değeri bir değişkene atadığında kopya mı oluşuyor, paylaşım mı?

Veri Yapıları kursu diziyi ve listeyi soyut veri tipi olarak kurdu ve gerçekleştirim
seçeneklerini karmaşıklıkla karşılaştırdı; o karşılaştırma burada tekrarlanmıyor. Burada
ölçülen Go'nun kendi iki tipinin — dizi ve dilim — atama sırasında **gözlemlenebilir**
davranışıdır: aynı `=` işareti, sağdaki değerin tipine göre iki ayrı şey yapıyor.

Dizi, dilim, sözlük ve yapı bu kursta **bileşik tip** ortak adıyla anılıyor, çünkü dördü de
başka tiplerden kurulu: bir dizi `int`lerden, bir yapı adlandırılmış alanlardan. Bileşik
tipler arasındaki fark, atama sırasında ne kadarının kopyalandığında yatıyor — ve bu ders o
farkın en temel iki üyesiyle, dizi ile dilimle, başlıyor.

#### Dizi: Sabit Uzunluk ve Kopyalanan Atama

Go'da bir dizinin uzunluğu tipin parçasıdır; `[4]int` ile `[3]int` iki ayrı tiptir, tıpkı
`int` ile `string`in iki ayrı tip olması gibi. Bu, "dört elemanlık bir dizi" gibi gevşek
bir tanım değil, derleyicinin sözdiziminden okuduğu kesin bir bilgidir.

**BT1.** — Uzunluğu farklı iki dizi tipi arasında doğrudan atama yapan bir kaynağın
derlenmediği burada bir derleyici çağrısıyla koşturulmuyor; ölçüm bunun yerine `%T`
biçimlendirme fiiliyle aynı bilgiyi dolaylı gösteriyor — basılan tip adının kendisi
uzunluğu içeriyor.

```go
// bilesik.go — dizi atamasi kopyalar, tip adi uzunlugu tasir
package main

import "fmt"

func main() {
	kaynakDizi := [4]int{10, 20, 30, 40}
	kopyaDizi := kaynakDizi
	kopyaDizi[0] = 99
	fmt.Println("tip           :", fmt.Sprintf("%T", kaynakDizi))
	fmt.Println("kaynakDizi    :", kaynakDizi)
	fmt.Println("kopyaDizi     :", kopyaDizi)
}
```

```
tip           : [4]int
kaynakDizi    : [10 20 30 40]
kopyaDizi     : [99 20 30 40]
```

Aynı kaynakta `kopyaDizi := kaynakDizi` satırının hemen üstüne `var kopyaDizi [3]int`
yazılsaydı derleme burada dururdu; `[3]int` ile `[4]int` birbirinin yerine geçmeyen iki tip
olduğu için atama reddedilirdi. Bu ders bunu ayrı bir derleyici çağrısıyla göstermiyor,
çünkü Go'nun Tasarım Amaçları dersi örtük tip dönüşümünün reddini zaten ölçmüştü; buradaki
fark yalnızca uzunluğun da tip kimliğinin bir parçası olduğudur.

Basılan tip `[4]int`tir, yalnızca `int` değil — uzunluk tip adının içinde durur.
`kopyaDizi := kaynakDizi` satırından sonra ilk öğeyi değiştirmek yalnızca `kopyaDizi`yi
etkiliyor; `kaynakDizi` değişmeden kalıyor. Dizi atandığında dört tam sayının **hepsi** yeni bir
değişkene kopyalanıyor — sözdizimi tek bir eşittir işareti olsa da, altında dört ayrı
kopyalama işlemi var.

Bu kopyalama yalnızca değişkenden değişkene atamada değil, bir fonksiyona argüman
geçirilirken de işliyor; parametreler de birer atamadır. Uzunluğu tipin parçası olmasının
bir başka sonucu da buradan çıkıyor: iki dizi, öğe tipleri karşılaştırılabilir olduğu
sürece, `==` ile doğrudan karşılaştırılabiliyor — çünkü karşılaştırılan şey bir işaretçi
değil, dizinin taşıdığı **içeriğin kendisi**. Aynı özellik dizinin bir sözlük anahtarı
olarak kullanılabilmesini de açıklıyor; Sözlükler dersinde görüleceği gibi bir sözlüğün
anahtar tipi karşılaştırılabilir olmak zorundadır ve dizi bu koşulu doğal olarak sağlar.
Aynı kural aşağı doğru da işliyor: bir alanı dilim olan bir yapı da karşılaştırılamaz hâle
gelir, çünkü yapının karşılaştırılabilirliği alanlarının karşılaştırılabilirliğine bağlıdır
— bu, Yapılar dersinde tekrar karşımıza çıkacak.

#### Dilim: Uzunluk, Kapasite ve Paylaşılan Alt Dizi

Dilim bir dizi değildir; dilimin kendisi üç parçadan oluşan küçük bir yapıdır: alt diziye
giden bir işaretçi, geçerli uzunluk ve kapasite. `[]int{10, 20, 30, 40}` yazımı bu üçünü
bir kerede kurar — arkasında dört öğelik bir alt dizi üretir ve dilim o alt diziyi gösterir.

```go
// paylasim.go — dilim atamasi basligi kopyalar, alt dizi paylasilir
package main

import "fmt"

func main() {
	kaynakDilim := []int{10, 20, 30, 40}
	payDilim := kaynakDilim
	payDilim[0] = 99
	fmt.Println("kaynakDilim   :", kaynakDilim)
	fmt.Println("payDilim      :", payDilim)
	fmt.Println("uzunluk       :", len(kaynakDilim), " kapasite:", cap(kaynakDilim))
}
```

```
kaynakDilim   : [99 20 30 40]
payDilim      : [99 20 30 40]
uzunluk       : 4  kapasite: 4
```

Aynı satır — `payDilim := kaynakDilim` — dizideki gibi bir kopya üretmiyor.
`payDilim[0] = 99` yazıldığında `kaynakDilim` de değişiyor, çünkü ikisi aynı alt diziyi gösteriyor; atanan
şey alt dizinin kendisi değil, ona giden işaretçiyi taşıyan küçük başlıktır. `uzunluk` ve
`kapasite` burada eşit çıkıyor çünkü dilim değişmezi tam olarak dört öğeyle kuruldu; bir
dilim dilimlemeyle türetildiğinde bu ikisi ayrışabilir — bu ayrım Dilim İşlemleri dersinin
konusu.

Başlığın küçük ve sabit boyutlu olmasının doğrudan bir sonucu var: bir dilimi kopyalamanın
maliyeti, alt dizinin kaç öğe tuttuğundan bağımsızdır. On öğelik bir dilimi atamak da on
milyon öğelik bir dilimi atamak da aynı üç alanı kopyalar; kopyalanan hiçbir zaman öğelerin
kendisi olmuyor. Diziyi atamanın maliyeti ise uzunlukla birlikte büyür, çünkü her öğe tek
tek kopyalanıyor.

Bu ayrım iki tipin nerede kullanılacağını da belirliyor. Uzunluğu koddan önce bilinen ve
hiç değişmeyecek küçük veriler — dört baytlık bir IPv4 adresi, sabit uzunluklu bir
sağlama toplamı — dizi olarak tanımlanır; boyutu tip imzasına yazmak, o boyutun asla
değişmeyeceğini derleyiciye söylemenin bir yoludur. Uzunluğu koşum sırasında değişecek her
şey — okunan satır sayısı, biriktirilen sonuç kümesi — dilim olarak tutulur, çünkü dilimin
uzunluğu tipin değil değerin bir alanıdır.

Dilimin `==` ile karşılaştırılamaması da aynı köke bağlanıyor. Bir dilim karşılaştırması
"içerik aynı mı" sorusuna yanıt vermeye kalksaydı derleyicinin her karşılaştırmada alt
diziyi baştan sona gezmesi gerekirdi; dil bunun yerine dilimi yalnızca `nil` ile
karşılaştırmaya izin veriyor, iki dilimi birbiriyle değil. Aynı `==` işleci dizide
derleniyor, dilimde derlenmiyor — çünkü dizi taşıdığı içeriğin kendisidir, dilim ise ona
giden bir göstericidir.

**BT2.** — Bu ayrım kaynak bir derleyici çağrısıyla koşturulur; basılan iki satır
yalnızca "derlendi" ya da "derlenmedi" sınıfını taşır, derleyicinin hata metni okunmaz.

```go
// karsilastirma.go — dizi == ile karsilastirilir, dilim karsilastirilamaz
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() {
	diziEsit := `package sinama
	func F() bool {
		a := [3]int{1, 2, 3}
		b := [3]int{1, 2, 3}
		return a == b
	}`
	dilimEsit := `package sinama
	func F() bool {
		a := []int{1, 2, 3}
		b := []int{1, 2, 3}
		return a == b
	}`
	fmt.Println("iki dizi == ile karsilastirilir :", gecerMi(diziEsit))
	fmt.Println("iki dilim == ile karsilastirilir:", gecerMi(dilimEsit))
}
```

```
iki dizi == ile karsilastirilir : true
iki dilim == ile karsilastirilir: false
```

#### Fonksiyona Geçerken Aynı Kural İşler

Parametre geçişi de bir atamadır ve yukarıdaki kural burada da aynen işliyor: dizi parametresi
kendi kopyasını alır; dilim parametresi aynı başlığı — dolayısıyla aynı alt diziyi — paylaşır. Bu,
iki değişken arasındaki atamayla fonksiyon çağrısı arasında Go'nun hiçbir ayrım yapmadığının bir
kanıtı: parametre bağlama da `=` işaretiyle aynı kuralı izliyor.

```go
// gecis.go — fonksiyona gecis de bir atamadir
package main

import "fmt"

func diziDegistir(d [4]int) {
	d[0] = 999
}

func dilimElemanDegistir(d []int) {
	d[0] = 999
}

func dilimYenidenAta(d []int) {
	d = append(d, 777, 777, 777, 777, 777) // kapasiteyi asar, yeni alt diziye gecer
}

func main() {
	dizi := [4]int{1, 2, 3, 4}
	diziDegistir(dizi)
	fmt.Println("dizi fonksiyona gecti  :", dizi)

	dilim := []int{1, 2, 3, 4}
	dilimElemanDegistir(dilim)
	fmt.Println("dilim eleman yazma     :", dilim)

	dilim2 := []int{1, 2, 3, 4}
	dilimYenidenAta(dilim2)
	fmt.Println("dilim yeniden atama    :", dilim2)
}
```

```
dizi fonksiyona gecti  : [1 2 3 4]
dilim eleman yazma     : [999 2 3 4]
dilim yeniden atama    : [1 2 3 4]
```

İlk satır beklendiği gibi: `diziDegistir` kendi kopyası üzerinde çalışıyor; çağıranın dizisi
değişmiyor. İkinci satır paylaşımı doğruluyor: `dilimElemanDegistir` parametresinin gösterdiği alt
diziye yazıyor ve bu yazma çağırana geri yansıyor. Üçüncü satır ise ince bir noktayı gösteriyor:
`dilimYenidenAta` parametreyi `append` ile büyütüp **yeniden atadığında** yalnızca kendi yerel
kopyasının başlığı değişiyor; çağırandaki `dilim2`nin başlığı hâlâ eski alt diziyi gösteriyor ve
fonksiyon döndüğünde o değişiklik kayboluyor. Fonksiyona bir dilim geçirmek alt diziyi paylaştırır
ama başlığı paylaştırmaz — bu, Sınırlayıcı Ölçüm bölümündeki header ayrılmasının fonksiyon çağrısı
bağlamındaki karşılığıdır.

Standart kitaplığın kendi `append` işlevi de bu yüzden bir sonuç döndürür ve çağıranın onu
geri atamasını bekler; işlev parametre olarak aldığı dilimi büyütüp kendi içinde tutsaydı
bile bu, çağırandaki değişkeni etkilemezdi. `dilimYenidenAta` işlevinin üçüncü satırdaki
davranışı, `append`in neden `s = append(s, x)` biçiminde çağrıldığının doğrudan kanıtıdır:
sonucu geri atamayı unutan kod, kapasite aşıldığında sessizce eski dilimle kalır.

#### Sınırlayıcı Ölçüm: Dilim Başlığı Ne Zaman Ayrılır

Dilimin paylaştığı şey alt dizidir, başlığın kendisi değil. Başlık da bir değerdir ve
atandığında kopyalanır — üç alanı (işaretçi, uzunluk, kapasite) birlikte. Bu fark, iki
değişken aynı alt diziyi göstermeyi bıraktığı anda görünür oluyor.

**BT3.** — Buradaki kapasite `make` ile açıkça istenmiyor; `[]int{1, 2, 3}` bir dilim
değişmezidir ve derleyici kapasiteyi üç öğeye eşitliyor. Ölçüm bu kapasiteyi sabit
varsaymıyor, koşumdan `cap` ile okuyor.

```go
// baslik.go — dilim basligi ne zaman kopyadan ayrilir
package main

import "fmt"

func main() {
	temel := []int{1, 2, 3}
	pay := temel
	pay[0] = 100
	fmt.Println("eleman yazmadan sonra   temel:", temel, " pay:", pay)

	oncekiKapasite := cap(temel)
	temel = append(temel, 4)
	pay[1] = 200
	fmt.Println("append sonrasi          temel:", temel, " pay:", pay)
	fmt.Println("kapasite (bu kosumda) onceki=", oncekiKapasite, " sonraki=", cap(temel))
}
```

```
eleman yazmadan sonra   temel: [100 2 3]  pay: [100 2 3]
append sonrasi          temel: [100 2 3 4]  pay: [100 200 3]
kapasite (bu kosumda) onceki= 3  sonraki= 6
```

İlk satırda `pay[0] = 100` hem `temel`i hem `pay`i değiştiriyor — ikisi hâlâ aynı alt
diziyi gösteriyor, tıpkı önceki bölümdeki gibi. Sonra `temel = append(temel, 4)` çağrısı
kapasiteyi aşıyor (üç öğelik alt dizide dördüncü öğeye yer yok); bu durumda dilin çalışma
zamanı **yeni** bir alt dizi ayırıyor, eski verileri oraya kopyalıyor ve `temel`in başlığını
o yeni alt diziyi gösterecek şekilde güncelliyor. `pay`in başlığı bu güncellemeden habersiz
kalıyor — hâlâ eski alt diziyi gösteriyor. Bu yüzden `pay[1] = 200` yalnızca `pay`i
değiştiriyor; `temel` üzerinde hiçbir iz bırakmıyor.

Bu geçişin öngörülemezliği asıl riski taşıyor. Kaynak koda bakan biri `pay[1] = 200`
satırının `temel`i etkileyip etkilemeyeceğini, yalnızca satırın kendisinden okuyarak
söyleyemez — cevap, o satıra gelene kadar `pay` ile `temel`in kaç kez ortak bir `append`
sınırını aştığına bağlıdır. İki dilim aynı alt diziyi paylaşırken biri büyütülüp diğeri
büyütülmediğinde, ikisi arasındaki paylaşım metinde hiçbir iz bırakmadan sona eriyor.

**BT4.** — Basılan iki kapasite sayısı (3 ve 6) bu koşumun gözlemidir, dilin sözü değildir.
Go belirtimi `append`in kapasiteyi aştığında yeni bir alt dizi ayıracağını garanti eder ama
yeni kapasitenin **ne kadar** büyüyeceğini belirtmez; büyüme oranı bir gerçekleştirim
ayrıntısıdır ve derleyici sürümleri arasında değişebilir. Kod bu satırı bir sabit gibi
okumamalı — yalnızca "kapasite aşıldı, yeni bir alt dizi geldi" bilgisini okumalı.

Kopyalanan şey burada iyice açık: `temel` ve `pay` hiçbir zaman aynı değişken olmadı,
ikisi de baştan beri kendi başlığına sahipti. Paylaşım, iki başlığın **aynı alt diziyi**
göstermesinden geliyordu; `append` alt diziyi değiştirdiğinde paylaşım bitiyor. Dilimin
kendisi bir değerdir — tıpkı dizinin bir değer olması gibi — ama taşıdığı değer alt dizinin
verisi değil, ona giden bir göstericidir.

Bunun pratikteki karşılığı şu: bir fonksiyon aldığı dilime `append` ile öğe ekliyorsa ve bu
büyümenin çağırana da yansımasını istiyorsa, büyümüş dilimi **geri döndürmek** zorundadır —
tıpkı `append`in kendi imzasının yaptığı gibi. Parametre olarak gelen dilim değişkeninin
kendisini büyütmek, yalnızca o yerel değişkeni günceller; çağırandaki başlık bundan
etkilenmez. Dizi ile dilim arasındaki fark böylece tek bir cümleye iniyor: dizi bir
değerdir ve tamamıyla kopyalanır, dilim de bir değerdir ama taşıdığı şey paylaşılan bir alt
diziye giden küçük bir başlıktır — ikisi de "referansla aktarım" değil, farklı içerikli bir
değerle aktarımdır. Bu ayrımın işaretçilerle ne ilgisi olduğu — bir değişkenin adresini
almanın ne zaman gerektiği, ne zaman gereksiz olduğu — İşaretçiler dersinde ayrıca ölçülüyor;
burada kurulan yalnızca dilimin kendi başlığının bir kopyalama biriminden ibaret olduğu.

#### Özet

- Dizinin uzunluğu tipin parçasıdır (`[4]int`); dizi atandığında bütün öğeler kopyalanır ve
  iki değişken birbirinden bağımsız hâle gelir.
- Dilim üç alanlı bir başlıktır: alt diziye giden işaretçi, uzunluk, kapasite. Dilim
  atandığında bu başlık kopyalanır ama alt dizi kopyalanmaz; iki değişken aynı alt diziyi
  paylaşır.
- Paylaşılan alt diziye yazılan bir değişiklik her iki değişkenden de görünür, çünkü ikisi
  de aynı belleği gösterir.
- `append` çağrısı kapasiteyi aştığında yeni bir alt dizi ayrılır ve yalnızca çağrının
  sonucunu tutan değişken o yeni alt diziye geçer; paylaşım burada kesilir.
- Kapasitenin büyüme oranı (3'ten 6'ya gibi) bir gerçekleştirim ayrıntısıdır; dilin sözü
  yalnızca "kapasite aşılınca yeni bir alt dizi ayrılır" ifadesidir.

#### Sonraki Adım

Bu derste paylaşım, yalnızca iki değişken aynı dilimi doğrudan taşıdığında görüldü —
`payDilim := kaynakDilim` gibi bir atamayla, ya da bir fonksiyona parametre olarak
geçirilerek. Ama bir dilim başka bir dilimden **dilimlenerek** türetildiğinde de aynı alt
dizi paylaşılır — ve bu durumda paylaşımın ne zaman süreceği, ne zaman keseceği yazılan
koda değil, o türetilen dilimin kapasitesine bağlıdır. Sıradaki ders bu bağımlılığı ölçer:
aynı `append` çağrısı, aynı kaynak metinle, iki ayrı kapasitede iki ayrı sonuç verir.

### Dilim İşlemleri

Source: https://academia.sh/tr/kurslar/go-temelleri/dilim-islemleri

Aynı append çağrısı, aynı kaynak metinle, iki ayrı kapasitede iki ayrı sonuç veriyor: kapasite sınırının içindeyse paylaşım sürüyor ve türetilen dilime yazmak kaynağı değiştiriyor, kapasite aşıldığında paylaşım kesiliyor ve kaynak dokunulmadan kalıyor. Bu açıklığı kapatan iki yazım var: copy ile alınan bağımsız kopya ve kapasiteyi açıkça sınırlayan üç indeksli dilimleme.

Önceki ders bir soruyla kapandı: aynı `append` çağrısı, aynı kaynak metinle, iki ayrı
kapasitede iki ayrı sonuç verir. Diziler ve Dilimler dersi paylaşımı yalnızca doğrudan
atamada gösterdi — `payDilim := kaynakDilim` gibi bir satırda. Bu ders paylaşımın daha
sık karşılaşılan biçimine bakıyor: bir dilimden **dilimlenerek** türetilen başka bir
dilime.

Dilimleme, Go kaynağında en sık görülen işlemlerden biridir: bir dosyanın satırlarını
gezerken, bir ağ paketinin başlığını gövdesinden ayırırken, bir metnin belirli bir
bölümünü alırken hep aynı sözdizim kullanılır. Bu yaygınlık, paylaşımın nasıl işlediğini
bilmemenin bedelini de büyütüyor — çünkü her dilimleme satırı, görünmeyen bir bağ
kuruyor.

#### Yazılmayan Dilim Sınırlarının Varsayılanı

Bir dilimleme ifadesi `kaynak[baslangic:bitis]` biçimindedir ve iki sınırdan biri ya da
ikisi de yazılmayabilir. Sıfır Değerler dersinin ölçtüğü on beş yazılmamışlığın biri tam
olarak buydu: dilim sınırlarının yazılmaması **tanımlıdır**, başlangıç yazılmadığında
`0`'a, bitiş yazılmadığında dilimin uzunluğuna karşılık gelir.

```go
// sinirlar.go — dilimleme sinirlarinin yazilmayan uclari
package main

import "fmt"

func main() {
	kaynak := []int{10, 20, 30, 40}
	fmt.Println("kaynak[:] :", kaynak[:])
	fmt.Println("kaynak[1:]:", kaynak[1:])
	fmt.Println("kaynak[:2]:", kaynak[:2])
}
```

```
kaynak[:] : [10 20 30 40]
kaynak[1:]: [20 30 40]
kaynak[:2]: [10 20]
```

Üçü de aynı `kaynak`tan türedi ve üçü de aynı alt diziyi gösteriyor; hiçbiri kopya
almıyor. `kaynak[:]` iki sınırı da varsayılana bırakıp bütün dilimi kapsıyor, `kaynak[1:]`
yalnızca başlangıcı, `kaynak[:2]` yalnızca bitişi yazıyor. Bu üç satırın ürettiği sayılar
Sıfır Değerler dersindeki bağlayıcı tabloyla aynıdır; orada 15 yazılmamışlığın 8'i
**tanımlı** sınıfına giriyordu ve dilim sınırları bunlardan biriydi.

Yazılmayan bir sınırla, **yanlış yazılmış** bir sınır aynı şey değildir. Bitiş boş
bırakıldığında dilimin uzunluğuna düşüyordu; bitiş açıkça yazılıp kapasiteyi aştığında ise
sonuç tanımlı bir değer değil, bir çalışma zamanı durmasıdır.

```go
// asim.go — yazilmayan sinir tanimli, kapasiteyi asan sinir acikta
package main

import "fmt"

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func main() {
	kaynak := []int{10, 20, 30, 40}
	gecerliPanikledi := panikliyorMu(func() { _ = kaynak[1:3] })
	fmt.Println("kaynak[1:3] (gecerli aralik)      panik=", gecerliPanikledi)
	asimPanikledi := panikliyorMu(func() { _ = kaynak[1:9] })
	fmt.Println("kaynak[1:9] (kapasiteyi asan bitis) panik=", asimPanikledi)
}
```

```
kaynak[1:3] (gecerli aralik)      panik= false
kaynak[1:9] (kapasiteyi asan bitis) panik= true
```

Boş bırakılan sınır ile fazla yazılan sınır burada iki ayrı sınıfa düşüyor: birincisi
dilin kendi doldurduğu tanımlı bir değer, ikincisi derleyicinin yakalayamadığı ve çalışma
zamanına bırakılan bir hata. Aradaki fark yazılıp yazılmaması değil, yazılan değerin
kapasitenin içinde kalıp kalmamasıdır.

#### Türetilen Dilime Yazmak Kaynağı Değiştirir

Dilimleme yeni bir alt dizi üretmez; türetilen dilim, kaynağın gösterdiği **aynı** alt
diziyi, farklı bir uzunluk ve başlangıç noktasıyla gösterir. Bu yüzden türetilen dilime
yazmak kaynağı da değiştirir.

```go
// turev.go — dilimlenerek turetilen dilime yazmak kaynagi degistirir
package main

import "fmt"

func main() {
	kaynak := []int{10, 20, 30, 40}
	turev := kaynak[1:3]
	turev[0] = 99
	fmt.Println("turev :", turev)
	fmt.Println("kaynak:", kaynak)
}
```

```
turev : [99 30]
kaynak: [10 99 30 40]
```

`turev := kaynak[1:3]` ikinci ve üçüncü öğeleri (`20`, `30`) kapsayan iki elemanlı bir
dilim üretiyor. `turev[0] = 99` yazıldığında değişen şey `turev`in kendi verisi değil,
`kaynak` ile paylaşılan alt dizinin ikinci öğesi; bu yüzden `kaynak[1]` de `99` oluyor. Bu,
Diziler ve Dilimler dersinde doğrudan atamada görülen paylaşımın aynısıdır — yalnızca
dilimin nasıl elde edildiği değişti, kuralın kendisi değişmedi.

Bu paylaşım ikiden fazla dilim arasında da sürüyor, çünkü hepsi aynı alt diziye bağlı
kalıyor. İki dilim kaynaktan **örtüşerek** türetildiğinde, örtüşen konuma yapılan bir yazma
ikisinden de görünüyor.

```go
// ortusen.go — kaynaktan ortusen iki dilim, ortak konuma yazma ikisinden de gorunur
package main

import "fmt"

func main() {
	kaynak := []int{10, 20, 30, 40, 50}
	sol := kaynak[0:3]
	sag := kaynak[2:5]
	sol[2] = 999 // sol'un son ogesi, kaynagin ucuncu ogesi, sag'in ilk ogesidir
	fmt.Println("sol   :", sol)
	fmt.Println("sag   :", sag)
	fmt.Println("kaynak:", kaynak)
}
```

```
sol   : [10 20 999]
sag   : [999 40 50]
kaynak: [10 20 999 40 50]
```

`sol` ilk üç öğeyi, `sag` son üç öğeyi kapsıyor; ikisi üçüncü konumda (`kaynak[2]`)
örtüşüyor. `sol[2] = 999` yazıldığında değişen tek bellek konumu, üçünün de — `sol`,
`sag` ve `kaynak`ın — paylaştığı o tek konum; bu yüzden değişiklik üçünde de görünüyor.
Kaç değişkenin aynı alt diziyi gösterdiğinin bir önemi yok, önemli olan yalnızca hangi
konumların örtüştüğü.

#### Aynı Çağrı, İki Kapasite, İki Sonuç

Buraya kadarki paylaşım öngörülebilirdi: bir dilim var oldukça, ondan türetilen her dilim
onunla aynı alt diziyi paylaşıyordu. `append` bu öngörülebilirliği bozuyor, çünkü
kapasiteyi aşıp aşmadığına göre iki farklı şey yapıyor: aşmıyorsa var olan alt diziye
yazıyor ve paylaşım sürüyor; aşıyorsa yeni bir alt dizi ayırıyor ve paylaşım kesiliyor.
Kaynak metin ikisinde de birebir aynı olabiliyor.

**BT5.** — İki dilim de aynı üç öğeyle, aynı sırayla dolduruluyor; farkı yaratan tek
değişken kapasitedir. `make`e ikinci argüman olarak kapasite açıkça veriliyor ki fark
koşumdan koşuma rastlantısal olmasın.

```go
// buyume.go — ayni append cagrisi, iki kapasite, iki sonuc
package main

import "fmt"

func ekle(s []int, deger int) []int {
	return append(s, deger)
}

func main() {
	bosluklu := make([]int, 3, 5) // kapasitede iki fazladan yer var
	copy(bosluklu, []int{1, 2, 3})
	dolu := make([]int, 3, 3) // kapasitede fazladan yer yok
	copy(dolu, []int{1, 2, 3})

	bosluklu2 := ekle(bosluklu, 4)
	bosluklu2[0] = 100
	fmt.Println("bosluklu (cap=5) sonra bosluklu:", bosluklu, " bosluklu2:", bosluklu2)

	dolu2 := ekle(dolu, 4)
	dolu2[0] = 100
	fmt.Println("dolu     (cap=3) sonra dolu:", dolu, " dolu2:", dolu2)
}
```

```
bosluklu (cap=5) sonra bosluklu: [100 2 3]  bosluklu2: [100 2 3 4]
dolu     (cap=3) sonra dolu: [1 2 3]  dolu2: [100 2 3 4]
```

`ekle` işlevi ikisine de **aynı** kaynak metinle çağrıldı: `append(s, deger)`. `bosluklu`
kapasitesinde iki fazladan yer taşıyordu; `append` bu yeri kullandı, alt diziyi büyütmedi
ve döndürdüğü `bosluklu2` hâlâ aynı alt diziyi gösteriyor. Bu yüzden `bosluklu2[0] = 100`
yazıldığında `bosluklu` da değişti — paylaşım sürdü. `dolu` ise tam kapasiteydi; `append`
yeni bir alt dizi ayırdı ve `dolu2` o yeni alt diziyi gösterdi. `dolu2[0] = 100` bu kez
yalnızca `dolu2`yi değiştirdi, `dolu` dokunulmadan kaldı.

Bu, kursun en net **açıkta** kalan satırıdır: dil `append`in kapasiteyi aştığında yeni bir
alt dizi ayıracağını garanti eder, ama sonucun kaynakla paylaşılıp paylaşılmayacağını
kaynak koddan okumak mümkün değildir. Aynı satır — `append(s, deger)` — bir çağrıda
paylaşımı sürdürüyor, bir çağrıda kesiyor; okuyan biri kapasiteyi bilmeden hangisinin
olacağını söyleyemez.

Bu belirsizliğin pratikte en çok karıştığı yer, bir dilimi büyüten işlevlerdir. Bir işlev
parametre olarak aldığı dilime `append` ile öğe ekleyip sonucu geri döndürmezse, çağırandaki
değişken kapasiteye bakılmaksızın **hiç** güncellenmez — kapasite yeterliyse bile.

**BT6.** — `kap`ın kapasitesi burada bilinçli olarak bol tutuluyor (on öğelik yer, üç öğe
eklenecek); amaç kapasite yetersizliğini olası açıklamalardan çıkarmak. Kapasite dar
tutulsaydı sonucun neden değişmediği iki nedene birden bağlanabilirdi, ölçüm belirsizleşirdi.

```go
// biriktir.go — sonucu dondurmeyen append, cagirani hicbir kosulda guncellemez
package main

import "fmt"

func ekleGuvenliDegil(hedef []int, kaynak []int) {
	for _, v := range kaynak {
		hedef = append(hedef, v)
	}
	fmt.Println("islev icinde hedef:", hedef)
}

func main() {
	kap := make([]int, 0, 10) // bol kapasiteli, bos bir birikec
	ekleGuvenliDegil(kap, []int{1, 2, 3})
	fmt.Println("cagirandaki kap   :", kap)
}
```

```
islev icinde hedef: [1 2 3]
cagirandaki kap   : []
```

`kap`ın kapasitesi ondu, üç öğe için fazlasıyla yeterliydi — önceki ölçümün `bosluklu`
örneğindeki gibi paylaşım sürebilirdi. Ama `ekleGuvenliDegil` her `append` sonucunu kendi
yerel `hedef` değişkenine atıyor ve hiçbir şey geri döndürmüyor; işlev döndüğünde
`kap`ın başlığı hiç değişmemiş oluyor, `len(kap)` hâlâ sıfır. Kapasitenin yeterli olması
bile bu durumda paylaşımı kurtarmıyor, çünkü sorun alt dizide değil, güncellenen başlığın
hiçbir zaman çağırana geri taşınmamasında.

#### copy ve Üç İndeksli Dilimleme: Açıklığı Kapatan İki Yazım

Bu öngörülemezlik iki yazımla kapatılabiliyor. Birincisi `copy` yerleşik işlevi: hedef ve
kaynağı ayrı alt dizilere sahip iki dilim olarak alır, öğeleri tek tek kopyalar ve kopyalanan
öğe sayısını döndürür.

```go
// kopya.go — copy ile bagimsiz kopya, uc indeksli dilimleme ile sinirli kapasite
package main

import "fmt"

func ekle(s []int, deger int) []int {
	return append(s, deger)
}

func main() {
	kaynak := []int{10, 20, 30, 40}

	bagimsiz := make([]int, len(kaynak))
	adet := copy(bagimsiz, kaynak)
	bagimsiz[0] = 7777
	fmt.Println("copy adet:", adet, " bagimsiz:", bagimsiz, " kaynak:", kaynak)

	sinirli := kaynak[1:3:3]
	fmt.Println("sinirli uzunluk=", len(sinirli), " kapasite=", cap(sinirli))
	sinirli2 := ekle(sinirli, 999)
	sinirli2[0] = 8888
	fmt.Println("sinirli2:", sinirli2, " kaynak:", kaynak)
}
```

```
copy adet: 4  bagimsiz: [7777 20 30 40]  kaynak: [10 20 30 40]
sinirli uzunluk= 2  kapasite= 2
sinirli2: [8888 30 999]  kaynak: [10 20 30 40]
```

`copy(bagimsiz, kaynak)` çağrısından sonra `bagimsiz` kendi alt dizisine sahip; ilk öğesini
değiştirmek `kaynak`ı hiç etkilemiyor. **BT7.** — Dönen `adet` değeri hiçbir yerde
varsayılmıyor, doğrudan basılıyor; `copy` hedefin ya da kaynağın uzunluğundan **küçüğünü**
kopyalar, bu örnekte ikisi de dört olduğu için dördü kopyalıyor.

`copy`nin hedef ve kaynağı bağımsız iki alt dizide olmak zorunda değil; ikisi **aynı** alt
diziyi gösterse bile doğru çalışıyor. Bu, öğeleri bir dilim içinde kaydırmak için işe
yarıyor.

```go
// kaydirma.go — copy ayni alt dizi icinde ortusen araliklarda da dogru calisir
package main

import "fmt"

func main() {
	dilim := []int{1, 2, 3, 4, 5}
	kaydirilan := copy(dilim[0:], dilim[1:4]) // ikinci-dorduncu ogeleri bir konum sola kaydir
	fmt.Println("kaydirilan adet:", kaydirilan, " dilim:", dilim)
}
```

```
kaydirilan adet: 3  dilim: [2 3 4 4 5]
```

`dilim[1:4]` ile `dilim[0:]` aynı alt dizinin örtüşen iki dilimidir; `copy` ikinci ile
dördüncü öğe arasındaki değerleri bir konum sola kaydırıyor ve son öğe (`5`) yerinde
kalıyor, çünkü kaydırma yalnızca kopyalanan üç öğeyi kapsadı. Bu davranış rastlantı değil,
`copy`nin kendi sözünün bir parçası: kaynak ve hedef örtüştüğünde bile sonuç, öğelerin
sırayla değil **doğru** kopyalandığı bir sonuçtur. Aynı işlemi tek tek dizin ilerleterek
elle yazan bir döngü, kaydırma yönüne göre öğelerin üzerine erken yazıp veriyi
bozabilirdi; `copy` bu riski taşımıyor.

İkinci yazım üç indeksli dilimlemedir: `kaynak[1:3:3]` yalnızca başlangıç ve bitişi değil,
üçüncü bir değerle **kapasiteyi** de belirtiyor. `sinirli`nin kapasitesi burada uzunluğuna
eşitleniyor — fazladan yer bırakılmıyor. Bu yüzden `sinirli`ye yapılan `append` çağrısı
**kesinlikle** kapasiteyi aşıyor ve yeni bir alt dizi ayırıyor; `sinirli2[0] = 8888`
yazıldığında `kaynak` dokunulmadan kalıyor. Önceki bölümdeki `dolu` örneğinde kapasitenin
tam olması bir rastlantıydı (üç öğelik değişmez tam üç kapasiteyle kuruldu); üç indeksli
dilimleme bunu **rastlantı olmaktan çıkarıp** bir yazım kararına dönüştürüyor — kapasiteyi
istenen sınıra kilitliyor.

**BT8.** — İki kapatma yazımı iki ayrı amaca hizmet ediyor: `copy` verinin tamamen ayrı bir
kopyasını istiyorsa kullanılır, üç indeksli dilimleme ise kaynakla aynı alt diziyi paylaşmayı
sürdürüp yalnızca sonraki bir `append`in paylaşımı kesmesini **garanti altına almak**
istendiğinde kullanılır. Birincisi paylaşımı baştan bitirir, ikincisi paylaşımı büyümenin
keseceğini önceden belirler.

Bu iki yazımın ortak noktası, ikisinin de kapasiteyi kod okuyucusunun tahminine
bırakmaması. `copy` kapasiteyi konudan tamamen çıkarıyor — hedefin kendi kapasitesi neyse
o kullanılıyor, kaynağınkiyle hiçbir ilişkisi kalmıyor. Üç indeksli dilimleme ise kapasiteyi
sıfırlamıyor, tam tersine onu **daraltıyor**: normalde `kaynak[1:3]` yazımı kalan bütün
kapasiteyi (kaynağın sonuna kadar) devralırdı, üçüncü indeks bu devralmayı keserek dilimi
kendi büyüklüğüne hapsediyor. İkisi de aynı sonuca varıyor — sonraki bir `append`in kaynağa
dokunmayacağından emin olmak — ama farklı maliyetlerle: `copy` bir kopyalama işlemi
gerektirir, üç indeksli dilimleme hiçbir veri taşımaz, yalnızca bir sayıyı sınırlar.

Üç bölümün ölçtüğü şey tek bir cümleye iniyor: dilimleme her zaman paylaşır; `append` kapasiteye
göre paylaşır ya da paylaşmaz; `copy` ve üç indeksli dilimleme ise paylaşımı yazım düzeyinde bir
karara dönüştürür. Kaynak metne bakarak "bu satır paylaşıyor mu" sorusunun yanıtı, üçüncü durumda
önceden bilinir; ilk ikisinde bilinmez, yalnızca koşturularak öğrenilir.

#### Özet

- Dilimleme ifadesinin yazılmayan sınırları tanımlıdır: başlangıç yazılmazsa `0`, bitiş
  yazılmazsa dilimin uzunluğu kullanılır; bu, Sıfır Değerler dersindeki bağlayıcı 8/4/3
  dağılımının tanımlı sınıfına giren bir örnektir.
- Dilimlemeyle türetilen bir dilim kaynakla aynı alt diziyi gösterir; türetilen dilime
  yazmak kaynağı da değiştirir.
- `append` kapasiteyi aşmadığı sürece var olan alt diziye yazar ve paylaşım sürer;
  kapasiteyi aştığında yeni bir alt dizi ayırır ve paylaşım kesilir. Aynı kaynak metin iki
  ayrı kapasitede iki ayrı sonuç verebilir.
- `copy` hedefe ait bağımsız bir alt diziye öğeleri tek tek kopyalar ve kopyalanan öğe
  sayısını döndürür; kaynakla hiçbir paylaşım bırakmaz.
- Üç indeksli dilimleme (`kaynak[baslangic:bitis:kapasite]`) kapasiteyi açıkça sınırlar ve
  sonraki bir `append`in kaynaktan ayrılacağını yazım düzeyinde garanti eder.

#### Sonraki Adım

Bu iki derste ölçülen paylaşım dizi ve dilim gibi sıralı yapılar arasındaydı; sıradaki
konu anahtar-değer eşleşmesine bakıyor. Sözlük de bir başlıktır ve bir alt yapıyı
paylaştırır, ama sorduğu soru farklıdır: olmayan bir anahtara erişildiğinde ne olur ve bu
yanıt tek bir değerle mi yoksa iki değerle mi geliyor? Sıradaki ders bu ikisini ayırıyor —
ve gezinme sırasının neden hiçbir zaman doğrudan yazdırılmadığını da.

### Sözlükler

Source: https://academia.sh/tr/kurslar/go-temelleri/sozlukler

Bir sözlükten okumak dört ayrı biçimde dört ayrı sınıfa düşüyor: var olan anahtar değerini veriyor, olmayan anahtar sıfır değerle birlikte bir yokluk yanıtı veriyor, nil bir sözlükten okumak tanımlı ama ona yazmak panikliyor, silinen bir anahtar yeniden yokmuş gibi davranıyor. Gezinme sırası bilerek rastgeledir ve hiçbir yerde doğrudan yazdırılmaz. Sınırlayıcı ölçüm iki yanıtlı erişimin neden gerekli olduğunu gösteriyor: değeri sıfır olan bir kayıt ile hiç var olmayan bir kayıt, tek yanıtlı erişimde ayırt edilemiyor.

Önceki ders dilimlerde paylaşımın ne zaman sürüp ne zaman kesileceğinin, kaynak koddan
değil kapasiteden okunduğunu ölçtü ve bunu ayırmayı ikinci bir soruya bıraktı: anahtar-değer
eşleşmesinde olmayan bir anahtara erişildiğinde ne olur ve bu yanıt tek bir değerle mi
geliyor, iki değerle mi? Bu ders o soruyla başlıyor.

Go'da anahtar-değer eşleşmesinin adı **sözlük**tür (`map`). Dilim gibi sözlük de bir
başlıktır — kendi verisini değil, çalışma zamanının yönettiği bir karma tabloya giden bir
göstericiyi taşır. Ama sözlüğün yazılmayan bir anahtara verdiği yanıt, dilimin yazılmayan
bir sınıra verdiği yanıttan daha katmanlı: dört ayrı erişim biçimi, dört ayrı sınıfa
düşüyor.

Bu katmanlılığın kaynağı, dilimin yalnızca **konum** üzerinden erişilmesine karşın
sözlüğün **anahtar** üzerinden erişilmesidir. Bir dilimde geçersiz bir konuma erişmek
her zaman aynı şeyi yapar — sınırların dışına taşan bir indeks panikler. Bir sözlükte
"anahtar yok" durumu panik değildir, çünkü sözlük olmayan bir anahtarı aramanın normal bir
sonuç olduğunu, tükenmiş bir sınır değil sıradan bir sorgu olduğunu varsayıyor. Panik yalnız
sözlüğün kendisi hiç var olmadığında (yazma denemesinde) geliyor.

#### Dört Erişim Biçimi

```go
// sozluk.go — sozlukte dort erisim bicimi, dort ayri sinif
package main

import "fmt"

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func main() {
	sayac := map[string]int{"kalem": 3, "defter": 7, "silgi": 1}

	deger, varMi := sayac["kalem"]
	fmt.Println("var olan anahtar    : deger=", deger, " varMi=", varMi)

	deger, varMi = sayac["mudur"]
	fmt.Println("olmayan anahtar     : deger=", deger, " varMi=", varMi)

	var bosSayac map[string]int
	fmt.Println("nil sozlukten okuma : deger=", bosSayac["herhangi"])

	yazmaPanikledi := panikliyorMu(func() { bosSayac["yeni"] = 1 })
	fmt.Println("nil sozluge yazma   : panik=", yazmaPanikledi)

	delete(sayac, "silgi")
	degerSil, varMiSil := sayac["silgi"]
	fmt.Println("silinen anahtar     : deger=", degerSil, " varMi=", varMiSil)
}
```

```
var olan anahtar    : deger= 3  varMi= true
olmayan anahtar     : deger= 0  varMi= false
nil sozlukten okuma : deger= 0
nil sozluge yazma   : panik= true
silinen anahtar     : deger= 0  varMi= false
```

Beş satır dört sınıfı ayırıyor. Kaynaktaki `deger, varMi := sayac["kalem"]` yazımının adı **iki
yanıtlı erişim**dir: sözlükten okumak, istenirse tek bir değer yerine değer ve varlık bilgisini
birlikte döndürür. Var olan bir anahtardan okumak beklendiği gibi çalışıyor. Olmayan bir anahtar
(`sayac` içinde hiç bulunmayan `"mudur"`) hata vermiyor, `int`in sıfır değeriyle birlikte bir
yokluk yanıtı veriyor — bu ikinci yanıt aşağıda ayrı bir bölümün konusu. Hiç ilk değeri verilmemiş
bir sözlükten — `bosSayak`, `nil` durumunda — okumak da çalışıyor ve yine sıfır değer dönüyor;
Sıfır Değerler dersinin bağlayıcı 8/4/3 dağılımında bu satır **tanımlı** sınıfına giren sekiz
durumdan biriydi. Ama aynı `nil` sözlüğe **yazmak** farklı bir sınıfa düşüyor: dil burada bir
sıfır değer koymuyor; çalışma zamanı panikliyor — aynı dağılımın **açıkta** kalan üç durumundan
biri. Son satır silinen bir anahtarın, hiç var olmamış bir anahtardan davranışça ayırt
edilemediğini gösteriyor: `delete` sonrası `sayac["silgi"]` de tıpkı `sayac["mudur"]` gibi sıfır
değer ve `false` veriyor.

Dört erişim biçiminin üçü aynı kalıba uyuyor — okumak her zaman güvenli, yazmak `nil`
sözlükte güvenli değil. Bu asimetri rastgele değil: bir sözlükten okumak yalnızca karma
tabloda arama yapar ve bulamazsa sıfır değer döndürür, ama yazmak karma tabloya bir girdi
eklemeyi gerektirir ve `nil` bir sözlüğün henüz **hiçbir** karma tablosu yoktur — eklenecek
yer, dilimin `nil` durumundaki gibi sıfır uzunluklu bir alt dizi değil, tümüyle **yok**. Bu
asimetri `delete`e kadar uzanıyor: `nil` bir sözlükten var olmayan bir anahtarı silmeye
çalışmak da panik vermiyor, çünkü `delete` de yazma gibi değil okuma gibi davranıyor —
silinecek bir şey bulamazsa sessizce hiçbir şey yapmıyor.

#### Anahtar Tipi Karşılaştırılabilir Olmak Zorundadır

Diziler ve Dilimler dersi bir dizinin `==` ile karşılaştırılabildiğini, bir dilimin
karşılaştırılamadığını ölçmüştü ve bunun bir sözlük anahtarı olarak kullanılabilirlikle
ilgili olduğunu bir cümleyle bırakmıştı. Sözlüğün kendi arama düzeneği bu bağlantıyı
zorunlu kılıyor: bir anahtarı bulmak için sözlük onu var olan anahtarlarla **eşitlik**
üzerinden karşılaştırıyor, bu yüzden anahtar tipi karşılaştırılabilir olmak zorunda.

**BT9.** — Bu kısıt bir derleyici çağrısıyla koşturuluyor; yalnızca "derlendi" ya da
"derlenmedi" sınıfı basılıyor.

```go
// anahtar.go — sozluk anahtari karsilastirilabilir olmak zorunda
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() {
	diziAnahtar := `package sinama
	func F() {
		m := map[[2]int]string{}
		m[[2]int{0, 0}] = "sol-ust"
	}`
	dilimAnahtar := `package sinama
	func F() {
		m := map[[]int]string{}
		_ = m
	}`
	fmt.Println("dizi anahtarli sozluk  :", gecerMi(diziAnahtar))
	fmt.Println("dilim anahtarli sozluk :", gecerMi(dilimAnahtar))
}
```

```
dizi anahtarli sozluk  : true
dilim anahtarli sozluk : false
```

Dizi anahtarlı sözlük derleniyor, çünkü dizinin karşılaştırılabilirliği önceki derste
kuruldu. Dilim anahtarlı sözlük derlenmiyor — dilim karşılaştırılamadığı için sözlüğün
kendisi bile **tanımlanamıyor**, tek bir girdi eklenmeden önce. Bu, iki bileşik tipin aynı
özelliğin (karşılaştırılabilirlik) iki ayrı yerde nasıl sonuç doğurduğunu gösteriyor: biri
`==` işlecinde, öteki bir sözlüğün anahtar tipi olarak kabul edilip edilmemesinde.

#### Fonksiyona Geçerken: Sözlük Dilimden Farklı Davranır

Önceki ders bir işlevin aldığı dilime `append` ile öğe eklemesinin, sonucu geri
döndürmediği sürece çağırana hiç yansımadığını ölçmüştü. Sözlükte aynı tuzak yok.

```go
// gecis.go — sozluge yazmak, sonucu dondurmeden de cagirana yansir
package main

import "fmt"

func ekleGuvenli(s map[string]int, anahtar string, deger int) {
	s[anahtar] = deger
}

func main() {
	sayac := map[string]int{"kalem": 3}
	ekleGuvenli(sayac, "silgi", 5)
	fmt.Println("cagirandaki sozluk:", sayac["silgi"])
}
```

```
cagirandaki sozluk: 5
```

`ekleGuvenli` hiçbir şey döndürmüyor, ama `sayac["silgi"]` çağırandan bakıldığında `5`.
Fark, dilimin `append`iyle sözlüğün yazma işleminin farklı şeyler yapmasından geliyor:
`append` kapasiteyi aştığında **yeni** bir alt dizi ayırıp başlığı günceller — bu güncelleme
yalnızca yerel kopyada olur. Sözlüğe yazmak ise var olan karma tabloyu **yerinde**
değiştirir; başlığın kendisi hiç değişmez, yalnızca tablonun içeriği değişir. Parametre
olarak geçen sözlük değişkeni çağırandakiyle aynı tabloyu gösterdiği için, tabloya yapılan
her değişiklik çağırana görünür. Sözlüğün dilimden ayrıldığı yer tam olarak burası: dilim
büyürken bazen alt dizisini değiştirmek zorunda kalıyor, sözlük ise büyürken kendi
tablosunu asla değiştirmiyor — yalnızca genişletiyor.

#### Kapasite İpucu, Kapasite Sınırı Değildir

`make` ile bir sözlük kurulurken verilebilen ikinci argüman, dilimdeki kapasiteyle aynı
sözü taşımıyor. Dilimde kapasite bir sınırdı — aşıldığında `append` yeni bir alt dizi
ayırırdı. Sözlükte aynı sayı yalnızca bir **ipucudur**; karma tablonun başlangıç
büyüklüğünü belirler ama sözlüğün büyümesini hiçbir şekilde sınırlamaz.

```go
// ipucu.go — sozluk kapasitesi bir ipuçudur, sinir degildir
package main

import "fmt"

func main() {
	kucuk := make(map[string]int, 2) // yalnizca baslangic ipucu
	for i, ad := range []string{"a", "b", "c", "d", "e", "f"} {
		kucuk[ad] = i
	}
	fmt.Println("baslangic ipucu=2, eklenen anahtar sayisi:", len(kucuk))
}
```

```
baslangic ipucu=2, eklenen anahtar sayisi: 6
```

`kucuk` yalnızca iki anahtar için ayrılmış bir ipucuyla kuruldu ama altı anahtar sorunsuz
eklendi. `make([]int, 0, 2)` yazımıyla kurulan bir dilime üçüncü öğeyi eklemek `append`i
yeni bir alt dizi ayırmaya zorlardı ve bu, Dilim İşlemleri dersinde ölçülen paylaşım
kesilmesine yol açardı; sözlükte böyle bir eşiği geçme anı yok, çünkü ikinci argüman
kapasiteyi **taahhüt etmiyor**, yalnızca çalışma zamanına bir tahmin veriyor. Sözlüğün
kendi iç tablosu gerektiğinde büyür, ama bu büyüme dilimin `append`i gibi çağırana
görünen bir başlık değişikliği doğurmaz — çünkü sözlüğün başlığı zaten çağıranla paylaşılan
tek başlıktır, yeniden atanacak ikinci bir kopyası yoktur.

#### Gezinme Sırası: Tanımlı Bir Belirsizlik

Bir sözlük üzerinde `range` ile gezinmenin sırası bilerek rastgeledir; aynı sözlük aynı
program içinde bile iki ayrı `range` çağrısında farklı sırayla dolaşılabilir. Bu bir
gerçekleştirim kusuru değil, **tanımlı bir belirsizliktir**: dil, gezinme sırasının hiçbir
koda bağımlı hâle gelmemesi için sırayı bilinçli olarak karıştırıyor. Bu yüzden kursun
hiçbir dersinde bir sözlük doğrudan yazdırılmıyor; bunun yerine anahtarlar sıralanıyor.

```go
// gezinme.go — sozluk dogrudan yazdirilmaz, anahtarlar siralanir
package main

import (
	"fmt"
	"sort"
)

func main() {
	sayac := map[string]int{"kalem": 3, "defter": 7, "silgi": 1}
	anahtarlar := make([]string, 0, len(sayac))
	for a := range sayac {
		anahtarlar = append(anahtarlar, a)
	}
	sort.Strings(anahtarlar)
	for _, a := range anahtarlar {
		fmt.Println(" ", a, "=", sayac[a])
	}
}
```

```
  defter = 7
  kalem = 3
  silgi = 1
```

Burada basılan sıra alfabetiktir, çünkü onu üreten `sort.Strings` çağrısıdır — sözlüğün
kendi gezinme sırası değil. `for a := range sayac` döngüsü yalnızca anahtarları bir dilime
topluyor; hangi sırayla topladığı bu ölçüm için önemsiz, çünkü `sort.Strings` sonucu her
koşumda aynı sıraya getiriyor.

Bilerek rastgele tutulan sıranın amacı, kodun sıraya **bağımlı hâle gelmesini** baştan
engellemektir. Sıra tutarlı olsaydı — örneğin her zaman ekleme sırasını izleseydi — bu
tutarlılığa güvenen kod zamanla birikirdi: bir döngü çıktısını ekleme sırasına göre
biçimlendiren, ya da iki sözlüğü aynı sırada gezip karşılaştıran kod. Böyle bir kod, karma
tablonun iç yapısı değiştiği anda (örneğin bir anahtar silinip yeniden eklendiğinde)
sessizce bozulurdu. Sırayı baştan rastgele tutmak, bu bağımlılığın hiç oluşmamasını
garantiliyor — programcı sıraya güvenemez, çünkü güvenilecek sabit bir sıra hiç yok.

Bir sözlüğü `fmt.Println(sayac)` ile doğrudan yazdırmak da teknik olarak çalışır ve hatta
biçimlendirme paketi çıktıyı anahtara göre sıralı üretebilir — ama bu, dilin garantisi
değil, biçimlendirme paketinin kendi seçimidir; `range` ile doğrudan gezinildiğinde aynı
garanti yoktur. Ölçüm bu farka güvenmiyor ve sıralamayı kendi elleriyle yapıyor; kursun
hiçbir dersi bir sözlüğü `range` çıktısına güvenerek yazdırmıyor.

**BT10.** — Sıralama üç anahtarlık küçük bir kümede yapılıyor; ölçümün amacı sıralamanın
kendisini göstermek değil, sözlüğün **kendi** sırasına hiç dayanılmadığını göstermek. Aynı
sonuç yüzlerce anahtarla da alınır, yalnızca çıktısı daha uzun olurdu.

#### Sınırlayıcı Ölçüm: Sıfır Değer ile Yokluk Tek Yanıtla Ayırt Edilemez

İki yanıtlı erişimin neden var olduğu, tek yanıtlı erişimin nerede yetersiz kaldığı
görülmeden tam anlaşılmıyor. Bir anahtarın değeri, tipin sıfır değeriyle **aynı** olduğunda
sözlük iki farklı durumu artık ayırt edemiyor: anahtar var ve değeri sıfır, ya da anahtar
hiç yok.

```go
// belirsizlik.go — sifir degerli kayit ile yokluk, tek yanitla ayirt edilemez
package main

import "fmt"

func main() {
	sayac := map[string]int{"kalem": 3}
	sayac["yeni-urun"] = 0 // kayit var, degeri kasten sifir
	tekYanit := sayac["yeni-urun"]
	tekYanitYok := sayac["hic-eklenmemis"]
	fmt.Println("var, degeri sifir olan   : tek yanit=", tekYanit)
	fmt.Println("hic eklenmemis           : tek yanit=", tekYanitYok)

	_, varMi1 := sayac["yeni-urun"]
	_, varMi2 := sayac["hic-eklenmemis"]
	fmt.Println("iki yanitla ayrim: yeni-urun varMi=", varMi1, " hic-eklenmemis varMi=", varMi2)
}
```

```
var, degeri sifir olan   : tek yanit= 0
hic eklenmemis           : tek yanit= 0
iki yanitla ayrim: yeni-urun varMi= true  hic-eklenmemis varMi= false
```

İlk iki satır birebir aynı: `0`. Tek yanıtlı erişimden bakıldığında `"yeni-urun"` ile
`"hic-eklenmemis"` arasında hiçbir fark yok — ikisi de sıfır veriyor. Üçüncü satır bu
körlüğü gideriyor: `varMi1` `true`, `varMi2` `false`. Fark, değerin kendisinde değil,
sözlüğün o anahtarı **hiç tutup tutmadığında**.

Bu sınırlama pratikte gerçek sonuçlar doğuruyor. Bir sayaç sözlüğünde "hiç görülmedi" ile
"görüldü ama sıfır kez arttı" arasında fark varsa, ya da bir yapılandırma haritasında
"varsayılan alınmış" ile "açıkça sıfıra ayarlanmış" arasında fark varsa, tek yanıtlı erişim
bu ayrımı hiç göremiyor. İki yanıtlı erişim burada zorunlu bir yazım değil, **tek** doğru
yazım hâline geliyor — "tanımlı" olmak, tek başına, "yeterli" olmak anlamına gelmiyor.

Bu sınırlama, dersin baştaki dört sınıfını da yeniden okumayı gerektiriyor. "Olmayan
anahtar" ile "silinen anahtar" bölümündeki iki satır tek yanıtla bakıldığında aynı `0`'ı
verdi; iki yanıtlı erişim eklenmeden önce bu ikisinin — ve değeri gerçekten sıfır olan
üçüncü bir anahtarın — birbirinden ayrılamayacağı görülmüyordu. Sözlüğün verdiği tek
yanıt, "bir değer var" ile "değer yok, yerine sıfır kondu" arasındaki farkı taşımıyor;
taşıyan, ikinci dönüş değeridir.

#### Özet

- Bir sözlükten dört erişim biçimi dört ayrı sınıfa düşüyor: var olan anahtar değerini
  veriyor, olmayan anahtar sıfır değerle bir yokluk yanıtı veriyor, `nil` sözlükten okumak
  tanımlı ama ona yazmak panikliyor, silinen bir anahtar yeniden yokmuş gibi davranıyor.
- Sözlüğe yazmak var olan tabloyu yerinde değiştirir; bir işleve geçirilen sözlüğe yapılan
  yazma, sonucu geri döndürmeden de çağırana yansır — dilimin `append` davranışından farklı.
- Sözlüğün gezinme sırası bilerek rastgeledir ve hiçbir derste doğrudan yazdırılmaz;
  anahtarlar `sort.Strings` ile sıralanıp öyle basılır.
- İki yanıtlı erişim, sıfır değerli bir kaydı yokluktan ayırmanın tek yoludur; tek yanıtlı
  erişim ikisini de aynı sıfır değerle döndürür.

#### Sonraki Adım

Sözlüğün anahtarı, tek bir tam sayı ya da dizgi gibi bölünmez bir değerdi; sayaç örneğindeki
gibi tek bir bilgiyi taşıyordu. Sıradaki tip birden çok alanı bir arada tutuyor ve kendi
alanlarının her birine ayrı ayrı erişilebiliyor — sözlüğün tek bir anahtarla eriştiği yerde,
bu yeni tip adlandırılmış birden çok alanla erişiyor. Yapı da yazılmayan bir şeyle başlıyor:
alanlarına değer verilmezse ne olur? Ama sözlükten farklı olarak alanların **sırası** da bir
anlam taşıyor ve bu sıraya güvenmenin kendi kırılganlığı var — sözlüğün anahtar adıyla
eriştiği yerde bu yeni tip, konumla erişmenin de bir seçenek olduğu bir alan taşıyor.

### Yapılar

Source: https://academia.sh/tr/kurslar/go-temelleri/yapilar

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?

### İşaretçiler

Source: https://academia.sh/tr/kurslar/go-temelleri/isaretciler

nil bir işaretçiden okumak açıkta kalıyor: dil bir şey koymuyor, derleyici de reddetmiyor, sonuç panik. Dört çağrının yalnızca ikisi çağıranın değerini değiştiriyor — adres alınmadan yazılan bir değer, fonksiyon parametresinde de yöntem alıcısında da yalnızca kendi kopyası üzerinde çalışıyor. Sınırlayıcı ölçüm işaretçinin de bir değer olduğunu gösteriyor: bir işaretçiyi yeniden atamak yalnızca yerel kopyayı değiştiriyor, gösterdiği yere yazmak çağıranı değiştiriyor.

Yapılar dersi bir yapının atandığında tamamen kopyalandığını, bir işlevin kendisine
geçirilen kopya üzerinde çalışırsa çağırandaki hiçbir şeyi değiştiremediğini kapattı. Bu
ders o sınırın nasıl aşıldığına bakıyor: bir değişkenin **adresini** almak, çağrılan tarafa
kopya yerine o adresi geçirmek anlamına geliyor — ve adres de kendi başına bir değer.

İşaretçi (`*T`), Go'da bir değişkenin bellekteki yerini tutan bir tiptir. `&x` yazımı
`x`in adresini üretir, `*p` yazımı bir işaretçinin gösterdiği yeri okur ya da oraya yazar.
Bu iki işleç — adres alma ve gösterileni çözme — bu dersin ölçtüğü her şeyin altında
duruyor: bir çağrının çağıranı değiştirip değiştirmemesi, hangisinin ne zaman yazıldığına
bağlı.

#### Ne Zaman Adres Almak Gerekir

Bu konudaki dört tip aynı soruya dört ayrı yanıt veriyor: bir işleve geçirilen değer
değiştirildiğinde, bu değişiklik çağırana adres almadan mı ulaşıyor, yoksa adres şart mı?

```go
// adres.go — dizi, dilim ve yapida adres almanin gerekip gerekmedigi
package main

import "fmt"

type Kayit struct {
	Sayi int
}

func degistirDizi(d [3]int)       { d[0] = 99 }
func degistirDiziPtr(d *[3]int)   { d[0] = 99 }
func yazDilimeElemanla(s []int)   { s[0] = 99 }
func yazSozluge(s map[string]int) { s["a"] = 99 }
func degistirYapi(k Kayit)        { k.Sayi = 99 }
func degistirYapiPtr(k *Kayit)    { k.Sayi = 99 }

func main() {
	dizi := [3]int{1, 2, 3}
	degistirDizi(dizi)
	fmt.Println("dizi, adressiz     :", dizi)

	dizi2 := [3]int{1, 2, 3}
	degistirDiziPtr(&dizi2)
	fmt.Println("dizi, adresle      :", dizi2)

	dilim := []int{1, 2, 3}
	yazDilimeElemanla(dilim)
	fmt.Println("dilim, adressiz    :", dilim)

	sozluk := map[string]int{"a": 1}
	yazSozluge(sozluk)
	fmt.Println("sozluk, adressiz   :", sozluk["a"])

	yapi := Kayit{Sayi: 1}
	degistirYapi(yapi)
	fmt.Println("yapi, adressiz     :", yapi)

	yapi2 := Kayit{Sayi: 1}
	degistirYapiPtr(&yapi2)
	fmt.Println("yapi, adresle      :", yapi2)
}
```

```
dizi, adressiz     : [1 2 3]
dizi, adresle      : [99 2 3]
dilim, adressiz    : [99 2 3]
sozluk, adressiz   : 99
yapi, adressiz     : {1}
yapi, adresle      : {99}
```

Altı satır dört bileşik tipin aynı soruya verdiği farklı yanıtı gösteriyor. Dizi ve yapı,
adres alınmadan geçirildiğinde değişmiyor — ikisi de tamamen kopyalanan değerler, Diziler
ve Dilimler ile Yapılar derslerinde kurulan kuralın aynısı. Dilim ve sözlük ise adres
**hiç alınmadan** değişebiliyor: `yazDilimeElemanla` ile `yazSozluge` yalnızca dilimin ve
sözlüğün kendisini (başlığını) aldı, işaretçi almadı, ama ikisi de çağırandaki veriyi
değiştirdi — çünkü değişen şey başlık değil, başlığın gösterdiği paylaşılan alt dizi ya da
karma tabloydu. Dilim İşlemleri ve Sözlükler derslerinde ölçülen paylaşım, burada adres
almanın **neden gereksiz** olduğunu açıklıyor: ikisi de kendi içinde zaten bir gösterici
taşıyor.

Dizi ve yapı için adres almanın gerekmesinin nedeni tam tersi: ikisi hiçbir göstericiyi
kendi içinde taşımıyor, taşıdıkları içeriğin kendisi. İşaretçi almak, bu içeriğin
kopyalanmasını engellemenin **tek** yolu. Dört tip böylece iki gruba ayrılıyor: dizi ve
yapı **kendi içeriğini taşıyor** ve değiştirilmek istendiğinde adres istiyor; dilim ve
sözlük **bir göstericiyi taşıyor** ve değiştirilmek istendiğinde kendi kopyası bile
yeterli oluyor — tabii büyümenin kendisi (`append`, ya da sözlüğün karma tablosunun ilk
kez kurulması) ayrı bir konu, çünkü o durumda değişen şey içerik değil başlığın kendisi.

#### nil İşaretçiden Okumak Açıkta Kalır

Bir işaretçi ilk değeri verilmeden tanımlandığında `nil` olur; bu, Sıfır Değerler
dersindeki bağlayıcı dağılımın **açıkta** kalan üç durumundan biriydi. `nil` işaretçinin
kendisini okumak (adresin ne olduğuna bakmak) sorun değil, ama gösterdiği yere gitmeye
çalışmak — alanına erişmek — çalışma zamanını durduruyor.

**BT14.** — Ölçüm iki ayrı soruyu birbirinden ayırıyor: işaretçinin kendisi `nil` mi
(güvenle sorulabilen bir soru) ve işaretçinin gösterdiği yerdeki alan ne (güvenle
sorulamayan bir soru). İkisi aynı satırda karıştırılmıyor.

```go
// nil.go — nil isaretciden okumak acikta kalir
package main

import "fmt"

type Kayit struct {
	Sayi int
}

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func main() {
	var isaretci *Kayit
	fmt.Println("isaretcinin kendisi nil mi:", isaretci == nil)

	okumaPanikledi := panikliyorMu(func() { _ = isaretci.Sayi })
	fmt.Println("isaretci.Sayi okumasi     : panik=", okumaPanikledi)
}
```

```
isaretcinin kendisi nil mi: true
isaretci.Sayi okumasi     : panik= true
```

`isaretci == nil` sorusunun kendisi güvenle yanıtlanıyor, çünkü bu, işaretçinin **kendi** değerini
okumak — bir adresi, ya da adresin yokluğunu. Ama `isaretci.Sayi` yazımı önce o adrese
**gitmeyi**, sonra oradaki `Sayi` alanını okumayı gerektiriyor; gidilecek adres yoksa bu adım
çalışma zamanında durduruyor. Sözlükler dersinde ölçülen `nil` sözlüğe yazma panikleri ile
buradaki fark: sözlükte panikleyen yalnızca **yazmaktı**; okumak tanımlıydı; işaretçide panikleyen
**okumanın kendisi**, çünkü sözlükte "aranan yer yok" tanımlı bir sonuca (sıfır değer)
bağlanabiliyordu, işaretçide "gidilecek yer yok"un tanımlı bir karşılığı bulunmuyor.

Bu fark, iki tipin `nil` durumunu neden farklı yorumladığını gösteriyor. Sözlüğün `nil`
durumu "henüz hiçbir karma tablom yok ama arama yapabilirim" demekti — arama bulunamayan
bir anahtar için zaten tanımlı bir yanıt (sıfır değer, `false`) üretiyordu. İşaretçinin
`nil` durumu ise "gösterdiğim hiçbir yer yok" demek; burada dönülecek tanımlı bir sıfır
değer yok, çünkü işaretçinin okunması gereken şey kendi değeri değil, gösterdiği yerdeki
değer. Sıfır Değerler dersindeki üç `açıkta` durumun ortak noktası tam olarak bu: tanımlı
bir karşılık, ancak sorulan sorunun **yapısı** buna izin verdiğinde var oluyor.

#### Dört Çağrı: Adres mi, Kopya mı

İşlevlere ek olarak, bir tipin **yöntemleri** de değer alıcı ya da işaretçi alıcı ile
tanımlanabilir; ikisi de nokta sözdizimiyle çağrılıyor ve çağrı yazımından hangisinin
kullanıldığı görünmüyor. Bir yöntem tanımı `func (k Kayit) Ad()` biçimindeyse alıcı
değerdir, `func (k *Kayit) Ad()` biçimindeyse alıcı işaretçidir; parantez içindeki bu tek
karakter (`*`), az önce ölçülen fonksiyon parametresi ayrımının birebir aynısını yöntemler
için de kuruyor. Ölçüm burada bilinçli olarak Yapılar dersindeki tek alanlı `Kayit` tipini
yeniden kullanıyor; farkı yaratan şeyin alanların sayısı değil, yalnızca alıcının tanımı
olduğunu görünür kılmak için.

```go
// dortcagri.go — dort cagri, ikisi cagiranin degerini degistiriyor
package main

import "fmt"

type Kayit struct {
	Sayi int
}

func kopyalaDegistir(k Kayit)   { k.Sayi = 99 }
func pointerDegistir(k *Kayit)  { k.Sayi = 99 }
func (k Kayit) DegistirV()      { k.Sayi = 99 }
func (k *Kayit) DegistirP()     { k.Sayi = 99 }

func main() {
	k1 := Kayit{Sayi: 1}
	kopyalaDegistir(k1)
	fmt.Println("deger parametreli fonksiyon   :", k1.Sayi)

	k2 := Kayit{Sayi: 1}
	pointerDegistir(&k2)
	fmt.Println("isaretci parametreli fonksiyon:", k2.Sayi)

	k3 := Kayit{Sayi: 1}
	k3.DegistirV()
	fmt.Println("deger alicili yontem          :", k3.Sayi)

	k4 := Kayit{Sayi: 1}
	k4.DegistirP()
	fmt.Println("isaretci alicili yontem       :", k4.Sayi)
}
```

```
deger parametreli fonksiyon   : 1
isaretci parametreli fonksiyon: 99
deger alicili yontem          : 1
isaretci alicili yontem       : 99
```

Dört çağrının **ikisi** çağıranın değerini değiştiriyor; ölçümün amacı bu ikiyi bulmak
değil, çağıranı değiştirmenin **çağrı yazımından değil alıcı tanımından** geldiğini
göstermek. İlk ikisi beklenen kalıbı
tekrarlıyor: değer parametresi kendi kopyasını alır, işaretçi parametresi çağırana geri
ulaşır. Son ikisi aynı kalıbı **yöntem çağrısı** biçiminde gösteriyor: `k3.DegistirV()` ile
`k4.DegistirP()` sözdizimsel olarak neredeyse özdeş — ikisi de `değişkenAdı.YöntemAdı()`
biçiminde — ama biri değer alıcı, öteki işaretçi alıcı ile tanımlı ve sonuçları farklı.
Çağrıya bakarak hangisinin çağırılacağı bilinmiyor; bunu belirleyen, yöntemin **kendi**
tanımındaki alıcı tipi.

`k4.DegistirP()` yazımının kendisi bir işaretçi içermiyor, ama derleyici `k4`ün
adresini **kendisi** alıp `(&k4).DegistirP()`e çeviriyor — çünkü `k4` adreslenebilir bir
değişken. Bu otomatik dönüşüm, işaretçi alıcılı bir yöntemi sıradan bir değer üzerinden
çağırmayı olanaklı kılıyor; adres alma adımı yazılmıyor ama gerçekleşiyor.

Bu otomatik dönüşümün bir sınırı var: yalnızca **adreslenebilir** değerler için işliyor.
Bir değişken adreslenebilir, çünkü bellekte kalıcı bir yeri var; bir işlevin dönüş
değeri gibi geçici bir değer değil. `k4.DegistirP()` çalıştı çünkü `k4` bir değişkendi;
`Kayit{Sayi: 1}.DegistirP()` gibi bir çağrı, süslü ayraçla kurulan değerin kendisi
adreslenebilir olmadığı için derlenmezdi. Aynı otomatik dönüşüm tersine de işliyor:
işaretçi alıcılı bir tipin `nil` olmayan bir işaretçisi üzerinden değer alıcılı bir
yöntem çağrıldığında, derleyici işaretçiyi kendiliğinden **çözüyor**.

Bu iki yönlü otomatik dönüşüm, bir tipin yöntemlerini hep aynı alıcı türüyle tanımlamayı
pratik bir alışkanlık hâline getiriyor: bir tipin bazı yöntemleri değer, bazıları işaretçi
alıcılıysa, çağıranın hangi çağrının çağırdığı değeri değiştirip hangisinin değiştirmediğini
yöntem yöntem ezberlemesi gerekiyor. Yukarıdaki `DegistirV`/`DegistirP` çifti bu karışıklığı
bilinçli olarak sergiliyor; gerçek bir tipte ikisinin bir arada bulunması, tam da bu ölçümün
gösterdiği belirsizliği üretir.

Alıcı seçimi yalnızca "çağıranı değiştirmek isteyip istememek" sorusuna bağlı değil. Bir
değer alıcı, yöntem her çağrıldığında o değerin **tam bir kopyasını** çıkarıyor — `Kayit`
gibi tek bir tam sayı taşıyan küçük bir tip için bu maliyet önemsiz, ama Yapılar dersindeki
gibi çok alanlı ya da başka yapıları gömen büyük bir tip için her çağrıda bütün alanları
kopyalamak anlamına geliyor. İşaretçi alıcı bu kopyalamayı, taşınan tek şey bir adres
olduğu için, yapının büyüklüğünden bağımsız kılıyor. Çağıranı değiştirmek istemeyen bir
yöntem bile, yalnızca kopyalama maliyetinden kaçınmak için işaretçi alıcı seçebiliyor.

#### Sınırlayıcı Ölçüm: İşaretçi de Bir Değerdir

İşaretçinin gösterdiği yere yazmakla, işaretçinin **kendisini** yeniden atamak farklı şeyler.
Go'da her şey değerle aktarılır (Programlama Temelleri kursunda kavram olarak kuruldu, burada
tekrarlanmıyor) — işaretçi de bir istisna değil, kendisi de bir değer ve bir işaretçi parametresi
de kopyalanıyor.

**BT15.** — İki işlev de birer tam sayı işaretçisi alıyor ve ikisi de kaynakta neredeyse
aynı görünüyor; fark tek bir satırda. Ölçüm bu iki satırı bilinçli olarak yan yana koyuyor
ki fark yalnızca yazıdan okunabilsin, çalıştırmaya gerek kalmasın — sonra ikisi ayrı ayrı
koşturulup bu okumanın doğru olduğu doğrulanıyor.

```go
// deger.go — isaretcinin kendisi de kopyalanan bir degerdir
package main

import "fmt"

func yenidenAta(p *int) {
	yeni := 99
	p = &yeni // yalnizca yerel kopyayi degistirir
}

func degerYaz(p *int) {
	*p = 99 // isaretcinin gosterdigi yere yazar
}

func main() {
	x := 1
	yenidenAta(&x)
	fmt.Println("isaretciyi yeniden atamak      :", x)

	y := 1
	degerYaz(&y)
	fmt.Println("isaretcinin gosterdigine yazmak:", y)
}
```

```
isaretciyi yeniden atamak      : 1
isaretcinin gosterdigine yazmak: 99
```

`yenidenAta` çağrıldığında `x`in adresi `p`ye kopyalanıyor; işlev içinde `p = &yeni`
yazıldığında değişen yalnızca `p`nin **kendi kopyası** — artık başka bir adresi gösteriyor,
ama çağırandaki `x` hiç etkilenmiyor, çünkü `x`in kendisi hiçbir zaman `yeni`nin adresini
almadı. `degerYaz` ise `p`nin gösterdiği adrese `*p = 99` ile doğrudan yazıyor; bu adres
hâlâ `x`inki ile aynı olduğu için (bu çağrıda `y`), yazma çağırana ulaşıyor.

Bu fark, dört çağrı ölçümündeki "işaretçi alınca değişir" kuralının **neden** doğru
olduğunu açıklıyor. İşaretçi parametreli bir fonksiyonun çağıranı değiştirmesi, işaretçinin
sihirli bir "referans" olmasından değil, işaretçinin **taşıdığı adresin** kopyalanıp
kopyalanan adrese yazılmasından geliyor. Adres kopyalansa bile gösterdiği yer aynı kaldığı
sürece, o yere yapılan yazma her kopyadan görünür — tıpkı dilimin başlığı kopyalandığında
alt dizinin paylaşılmaya devam etmesi gibi. İşaretçi, kopyalandığında paylaşımı sürdüren bir
değerdir; farkı, dilimin aksine, gösterdiği şeyin bir alt dizi değil **tek bir değer**
olmasıdır.

Bu ders boyunca ölçülen dört tip — dizi, dilim, sözlük, yapı — ve işaretçinin kendisi,
aynı tek kuralın beş farklı görünümüdür: Go'da atama, parametre geçişi ve dönüş hep aynı
işi yapar, bir değeri kopyalar. Görünürdeki fark, kopyalanan değerin **ne taşıdığından**
geliyor. Dizi ve yapı içeriği taşıyor, kopya içeriği de kopyalıyor. Dilim ve sözlük bir
göstericiyi taşıyor, kopya göstericiyi de kopyalıyor ama gösterilen yeri değil — bu yüzden
paylaşım sürüyor. İşaretçi de aynı kalıba uyuyor: taşıdığı tek şey bir adres, kopya adresi
kopyalıyor, adresin gösterdiği yeri değil. "Referansla aktarım" hiçbir zaman ayrı bir
düzenek olarak var olmadı; var olan, bazı değerlerin bir gösterici taşıması ve bazılarının
taşımamasıydı.

#### Özet

- Dizi ve yapı adres alınmadan geçirildiğinde değişmez, çünkü ikisi de tamamen kopyalanır;
  dilim adres almadan da değişebilir, çünkü kendi içinde zaten bir gösterici taşır.
- `nil` bir işaretçinin kendisini okumak güvenlidir; gösterdiği yere gitmeye çalışmak
  (bir alana erişmek) çalışma zamanını durdurur — Sıfır Değerler dersindeki açıkta
  sınıfının bir örneği.
- Dört çağrının (değer parametresi, işaretçi parametresi, değer alıcılı yöntem, işaretçi
  alıcılı yöntem) yalnızca ikisi çağıranın değerini değiştirir; ayrım nokta sözdiziminden
  değil, alıcının tanımından okunur.
- İşaretçi kendisi de bir değerdir ve kopyalanır: bir işaretçiyi yeniden atamak yalnızca
  yerel kopyayı değiştirir, gösterdiği yere yazmak (`*p = ...`) çağırana ulaşır.

#### Sonraki Adım

Bu derste ölçülen tipler — dizi, dilim, yapı, işaretçi — hep sayı ya da adlandırılmış alan
taşıyordu; hiçbiri kendi içinde metin taşımıyordu. Metin bunlardan farklı bir bileşik tip:
bir dizgi, dışarıdan bakıldığında tek parça görünen ama içeride bir bayt dizisi olan bir
değer. Dizgi de değiştirilemez olduğu için burada ölçülen kopya/paylaşım ayrımının dışında
kalıyor, ama kendi yazılmayan sorusunu taşıyor. Sıradaki ders şu soruyu soruyor: bir
dizginin uzunluğu neyi sayıyor, bir dizgiyi indekslemek neyi döndürüyor ve bu ikisi
"karakter" sözcüğünün sezgisiyle ne zaman ayrışıyor?

### Dizgiler ve Rune'lar

Source: https://academia.sh/tr/kurslar/go-temelleri/dizgiler-ve-runelar

Bir Go dizgisi bir bayt dizisidir: uzunluk bayt sayar, indeksleme bir bayt döndürür, yalnızca range bir rune verir. Aynı metnin bayt sayısı, rune sayısı ve ilk baytı birbirinden ayrı ölçülüyor ve dizginin değiştirilemez olduğu gösteriliyor. Sınırlayıcı ölçüm rune sayısının da karakter sayısı olmadığını gösteriyor: birleşik işaretler görünüşte aynı metni farklı rune sayılarıyla üretebiliyor.

Bu konu boyunca ölçülen tipler — dizi, dilim, sözlük, yapı, işaretçi — hep sayı, adres ya
da adlandırılmış alan taşıdı. Dizgi bunlardan ayrı bir soru soruyor: dışarıdan bakıldığında
tek parça görünen bir metin, içeride **ne** taşıyor? Yanıt, bu konunun kapanış sorusu:
dizginin uzunluğu neyi sayıyor, bir dizgiyi indekslemek neyi döndürüyor?

Go'nun dizgi tipi, dilin standart kitaplığındaki metin işleme araçlarının hepsinin altında
duruyor: bir dosya yolu, bir ağ isteğinin gövdesi, bir kullanıcı girdisi — hepsi dizgi
olarak taşınıyor. Bu tipin **ne** taşıdığını bilmemek, bu araçların hangi birimde
çalıştığını (bayt mı, kod noktası mı) bilmemek anlamına geliyor.

Dizgi de bir bileşik tiptir, ama bu konudaki ötekilerden iki yönden ayrılıyor. Birincisi,
şimdiye kadar ölçülen her tip (dizi hariç) bir şekilde değiştirilebilirdi — dilime
yazılabiliyordu, sözlüğe eklenebiliyordu, yapının alanı güncellenebiliyordu; dizgi hiçbir
biçimde yerinde değiştirilemiyor. İkincisi, dizginin taşıdığı veri kendi başına anlamsız
baytlar değil, bir **kodlama kuralına** (UTF-8) göre yorumlanan bir bayt dizisi; bu kural,
uzunluğun ve indekslemenin ne anlama geldiğini doğrudan belirliyor.

#### Dizgi Bir Bayt Dizisidir

Go'da bir dizgi, salt okunur bir bayt dizisidir. Kaynak dosya UTF-8 olduğu için dizgi
**değişmezleri** UTF-8'dir, ama çalışma zamanındaki bir dizginin geçerli UTF-8 taşıdığı dil
tarafından garanti edilmez. `len` bir dizgiye uygulandığında bayt sayısını verir, indeksleme
(`metin[i]`) tek bir baytı döndürür — bir karakteri değil. Bu, Sıfır Değerler dersindeki bağlayıcı
dağılımın **açıkta** kalan üç durumundan biriydi: dizginin uzunluğu tanımlı ama indekslendiğinde
gelen şey karakter değil bayttır.

**BT16.** — `"çay"` özellikle **Türkçe** bir sözcük olarak seçildi, çünkü `ç` gibi tek
baytla kodlanamayan bir harf içeriyor; yalnızca ASCII harflerinden oluşan bir metin bu
ölçümdeki bayt/rune farkını hiç göstermezdi.

```go
// bayt.go — dizginin uzunlugu ve indekslemesi bayt uzerinden calisir
package main

import "fmt"

func main() {
	metin := "çay"
	fmt.Println("uzunluk (bayt):", len(metin))
	fmt.Println("ilk bayt      :", metin[0])
	fmt.Println("rune sayisi   :", len([]rune(metin)))
}
```

```
uzunluk (bayt): 4
ilk bayt      : 195
rune sayisi   : 3
```

`"çay"` üç harf gibi görünüyor ama `len` `4` veriyor, çünkü `ç` harfi UTF-8'de **iki**
bayta kodlanıyor (`195` ve bir sonraki bayt), `a` ile `y` birer bayta. `metin[0]` bir
karakter değil, `ç`nin ilk baytının sayısal değerini (`195`) veriyor — `fmt.Println` bunu
bir tam sayı olarak basıyor, dizgi parçası olarak değil. `[]rune(metin)` dönüşümü ise
dizgiyi kod noktalarına ayırıyor ve üç rune buluyor — görünen harf sayısıyla örtüşen sayı
bu, bayt sayısı değil.

Bu üç sayının (`4`, `195`, `3`) birbirinden bağımsız okunması gerekiyor. `len(metin)` her
zaman baytı sayar, kaç harf görünürse görünsün; salt ASCII bir dizgide bayt sayısı ile
rune sayısı çakışır, ama bu bir rastlantıdır — ASCII'nin her karakteri zaten tek bayta
kodlandığı için. Türkçe, Yunanca, Kiril ya da herhangi bir çok baytlı alfabe içeren bir
dizgide bu çakışma bozuluyor; `"çay"` üç harfken dört bayt olması tam olarak bu bozulmanın
kendisi.

#### Dizgi Değiştirilemez

İşaretçiler dersinin sonunda bırakılan soru burada yanıtını buluyor: dizgi bu konudaki
kopya/paylaşım ayrımının dışında kalıyor, çünkü dizgi **değiştirilemez**. Bir dizginin
tek bir baytını bile yerinde değiştirmek mümkün değil. Bu, dilim ile dizginin arasındaki
en görünür farktır: ikisi de bir bayt (ya da öğe) dizisine giden bir başlık taşır, ama
dilimin başlığının gösterdiği alt diziye yazmak serbestken, dizginin başlığının gösterdiği
bayt dizisine yazmak dilin kendisi tarafından engellenir.

```go
// degistirilemez.go — dizginin bir baytina yazmak reddedilir
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() {
	yazma := `package sinama
	func F() {
		s := "abc"
		s[0] = 'x'
		_ = s
	}`
	fmt.Println("dizgi indeksine yazma:", gecerMi(yazma))
}
```

```
dizgi indeksine yazma: false
```

`s[0]` bir bayt **okumak** için geçerli bir yazım, ama `s[0] = 'x'` reddediliyor —
derleyici dizginin öğelerine yazmaya izin vermiyor. Bu, Diziler ve Dilimler dersindeki
dizi indeksine yazmaktan temelde farklı: dizi öğesine yazmak geçerlidir, dizgi baytına
yazmak hiçbir zaman geçerli değildir. Bir dizgiyi "değiştirmek" isteyen kod, aslında
**yeni** bir dizgi üretiyor — orijinali olduğu gibi bırakıp, değiştirilmiş bir kopyayı bir
değişkene yeniden atıyor. Bu yüzden dizgi, bu konudaki hiçbir derste ölçülen "türetilen bir
parçaya yazınca kaynak da değişiyor mu" sorusuna hiç muhatap olmuyor — yazmanın kendisi
mümkün değil.

**BT17.** — Bu ölçüm de bir derleyici çağrısıyla koşturuluyor, çünkü ret derleme
zamanında geliyor; çalışma zamanına bırakılan bir panik değil.

Bir dizgiyi baytlar üzerinde değiştirmek isteyen kod, `[]byte` dönüşümüyle **ayrı** bir
kopya çıkarıyor; o kopya değiştirilebilir ama orijinal dizgi bundan etkilenmiyor.

```go
// donusum.go — []byte donusumu ayri bir kopya cikarir
package main

import "fmt"

func main() {
	metin := "çay"
	baytlar := []byte(metin)
	baytlar[2] = 'X' // ucuncu bayt 'a' harfidir, tek bayt kaplar
	fmt.Println("degistirilen kopya:", string(baytlar))
	fmt.Println("orijinal dizgi    :", metin)

	parca1 := "çay"
	birlesik := parca1 + " bahcesi"
	fmt.Println("birlesik dizgi    :", birlesik)
	fmt.Println("parca1 degismedi mi:", parca1 == "çay")
}
```

```
degistirilen kopya: çXy
orijinal dizgi    : çay
birlesik dizgi    : çay bahcesi
parca1 degismedi mi: true
```

`[]byte(metin)` yazımı `metin`in baytlarını **yeni** bir dilime kopyalıyor; bu dilim
sıradan bir dilim gibi değiştirilebilir, üçüncü baytını `'X'` yapmak dilimin kendi verisini
değiştiriyor. Ama `metin` bundan hiç etkilenmiyor, çünkü dönüşüm bir paylaşım değil bir
kopyalama. `+` ile birleştirme de benzer: `parca1 + " bahcesi"` yeni bir dizgi üretiyor,
`parca1`in kendisi hiç değişmiyor. Diziler ve Dilimler dersindeki dizi gibi, dizgi de
"türetilmiş" hiçbir yolla kaynağa geri yazılamıyor — dilim ve sözlükten ayrıldığı yer tam
olarak burası.

#### range: Bayt İndeksi, Rune Değeri

`len` ve indeksleme bayt üzerinden çalışıyordu; bir dizgi üzerinde `range` ile gezinmek
ise farklı davranıyor. `range`, dizgiyi UTF-8 kurallarına göre çözüyor ve her adımda bir
**rune** veriyor, tek bir bayt değil. `len` bayt sayıyor, `metin[i]` bir bayt veriyor,
`range` bir rune veriyor — üçü de aynı veriden, üç ayrı birimde okuyor.

```go
// gezinme.go — range dizgide bayt degil rune verir
package main

import "fmt"

func main() {
	metin := "çay"
	for i, r := range metin {
		fmt.Printf("  bayt-indeks=%d rune=%q kod-noktasi=U+%04X\n", i, r, r)
	}
}
```

```
  bayt-indeks=0 rune='ç' kod-noktasi=U+00E7
  bayt-indeks=2 rune='a' kod-noktasi=U+0061
  bayt-indeks=3 rune='y' kod-noktasi=U+0079
```

Üç adım, üç rune veriyor — `metin[0]`in verdiği tek bayt (`195`) değil, tam bir kod noktası (`ç`,
U+00E7). Basılan indeksler de ardışık değil: `0`, `2`, `3` — ikinci adım `1`i atlıyor, çünkü `ç`
iki bayt kaplıyor ve `range` bir sonraki rune'un **bayt konumuna** atlıyor. `rune`, dilin kendi
tip adıdır ve `int32`nin bir takma adıdır; bir Unicode kod noktasını taşır. Kod noktasının kendisi
Bilgisayarlar Nasıl Çalışır kursunda kapatılmış bir kavramdır; burada ölçülen, Go'nun bu kavramı
`rune` adıyla ve `range`in otomatik çözümüyle nasıl gerçekleştirdiğidir.

Bu otomatik çözüm yalnızca `range`e özgü; dizgi başka bir bileşik tipe **dönüştürülerek**
gezinilirse aynı çözüm uygulanmıyor. `[]byte(metin)` üzerinde `range` yapmak, `metin`in
kendisi üzerinde `range` yapmaktan farklı sonuç veriyor.

```go
// bayt-gezinme.go — []byte uzerinde range bayt bayt gezinir
package main

import "fmt"

func main() {
	metin := "çay"
	for i, b := range []byte(metin) {
		fmt.Printf("  indeks=%d bayt=%d\n", i, b)
	}
}
```

```
  indeks=0 bayt=195
  indeks=1 bayt=167
  indeks=2 bayt=97
  indeks=3 bayt=121
```

Burada dört adım var, önceki bölümdeki üç adım değil; indeksler `0, 1, 2, 3` — ardışık,
atlamasız. `range`in rune'lara çözme davranışı yalnızca `range`e verilen değer bir **dizgi**
olduğunda geçerli; aynı veriyi `[]byte`ye çevirip verdiğinde `range` sıradan bir dilim
üzerinde gezindiğini varsayıyor ve tek tek baytları veriyor. Fark kaynaktaki verinin
kendisinde değil, `range`e verilen **tipte**.

#### Sınırlayıcı Ölçüm: Rune Sayısı da Karakter Sayısı Değildir

`len([]rune(metin))` görünen harf sayısını verdi ve bu, bayt sayısından daha güvenilir
görünüyor. Ama rune sayısı da "karakter sayısı" ile her zaman örtüşmüyor, çünkü Unicode'da
görünüşte tek bir işaret, **birden çok** kod noktasının birleşimiyle de yazılabiliyor.

**BT18.** — İki dizgi de kaynakta birebir aynı görünen dört harfi (`c`, `a`, `f`,
`e`-türevi bir işaret) taşıyor; ölçüm ikisini yalnızca kod noktası düzeyinde ayırıyor,
ekrandaki görüntüyü değil.

```go
// birlesim.go — gorunuste ayni metin, iki ayri rune sayisi
package main

import "fmt"

func main() {
	onceden := "café"    // e ve aksan tek kod noktasinda birlesik: U+00E9
	sonradan := "cafe" + "́" // e ayri, aksan ayri kod noktasi: U+0065 U+0301
	fmt.Println("onceden bilesimli:", onceden, " rune sayisi=", len([]rune(onceden)), " bayt=", len(onceden))
	fmt.Println("sonradan bilesen :", sonradan, " rune sayisi=", len([]rune(sonradan)), " bayt=", len(sonradan))
	fmt.Println("bayt bayt esit mi:", onceden == sonradan)
}
```

```
onceden bilesimli: café  rune sayisi= 4  bayt= 5
sonradan bilesen : café  rune sayisi= 5  bayt= 6
bayt bayt esit mi: false
```

İki dizgi de ekranda **aynı** görünüyor — ikisi de "café" yazıyor. Ama `onceden` tek bir
önceden birleşik kod noktası (`é`, U+00E9) kullanırken, `sonradan` düz `e`yi ayrı bir
**birleşen aksan** kod noktasıyla (U+0301) izliyor; ikisi görsel olarak aynı işareti
üretiyor ama farklı sayıda kod noktasıyla. Bu yüzden `onceden` dört rune, `sonradan` beş
rune sayıyor ve `==` ile karşılaştırıldıklarında eşit çıkmıyorlar — dizgi karşılaştırması
bayt bayt yapılıyor, görsel eşdeğerliğe bakmıyor. Diziler ve Dilimler dersinde ölçülen
`==` işlecinin dizide içeriğe baktığı kuralı burada da geçerli: dizgi karşılaştırması da
içeriğin (baytların) birebir eşitliğine bakıyor, ekranda nasıl göründüğüne değil. İki
dizgi görsel olarak ayırt edilemez olsa bile, farklı kod noktası dizilimine sahiplerse
`==` bunları farklı sayıyor. Bu, kullanıcı girdisiyle çalışan bir kodun karşılaşabileceği
somut bir sorun: aynı görünen iki metin, kaynakları farklı olduğunda (biri klavyeden,
biri bir dosyadan, biri başka bir sistemden gelmiş olabilir) `==` ile karşılaştırıldığında
eşit çıkmayabiliyor — hatanın nedeni karşılaştırma işlecinde değil, iki girdinin baştan
farklı kod noktalarıyla kodlanmış olmasında.

Bu sınır, "görünen işaret" birimine dilin kendisinde bir karşılık olmadığını gösteriyor.
Bilgisayarlar Nasıl Çalışır kursunda kapatılan **grafem** kavramı tam olarak bunu
adlandırıyor: bir ya da daha çok kod noktasının bir araya gelip kullanıcının tek bir
işaret olarak gördüğü şeyi oluşturması. Go'nun standart kitaplığı rune sayma dışında bir
araç sunmuyor; grafem sayımı, kod noktalarını birleştirme kurallarını uygulayan ayrı bir
adım gerektiriyor ve bu adım bu ölçümün kapsamı dışında.

Üç ölçü birimi böylece üç ayrı soruya yanıt veriyor ve hiçbiri ötekinin yerini
tutmuyor. Bayt sayısı, bir dizginin bellekte ya da bir ağ üzerinden ne kadar yer
kapladığını söylüyor — depolama ve aktarım burada önemli. Rune sayısı, kaç kod
noktasının art arda geldiğini söylüyor — dilin kendi tip sisteminin sınırı burada. Grafem
sayısı ise bir kullanıcının ekranda kaç "işaret" gördüğünü söylüyor, ama bu bilgi dilin
tip sisteminde hiç durmuyor; bir dizginin kendisine bakarak elde edilemiyor. `len`in bayt
saydığını bilmeyen bir kod bir dizgiyi ortadan kesip geçersiz bir UTF-8 dizisi
üretebilir; rune sayısının grafem sayısı olmadığını bilmeyen bir kod da "beş karakterle
sınırla" gibi bir kuralı birleşik işaretli metinlerde yanlış uygulayabilir. İkisi de aynı
kökten geliyor: dizginin **kendi** ölçü birimini, kullanıcının **gördüğü** ölçü birimiyle
karıştırmak.

#### Özet

- Bir Go dizgisi bir bayt dizisidir: `len` bayt sayar, indeksleme bir bayt döndürür —
  Sıfır Değerler dersindeki açıkta kalan üç durumdan biri buydu.
- Dizgi değiştirilemez; bir bayta yazmak derleme zamanında reddedilir. "Değiştirme" her
  zaman yeni bir dizgi üretmek anlamına gelir; `[]byte` dönüşümü bile orijinali paylaşmaz,
  bağımsız bir kopya çıkarır.
- `range` ile gezinmek bayt değil **rune** verir; bayt indeksleri çok baytlı kod
  noktalarında ardışık olmayabilir.
- Rune sayısı bayt sayısından daha güvenilir bir "karakter" ölçüsü gibi görünse de,
  görünüşte aynı metin birleşik ya da ayrı kod noktalarıyla farklı rune sayıları üretebilir
  — "karakter" dediğimiz şeyin dilde doğrudan bir karşılığı yok, grafem kavramı bu boşluğu
  adlandırıyor.

#### Sonraki Adım

Bu konu, dizi ile başlayıp dilim, sözlük, yapı, işaretçi ve son olarak dizgiyle kapandı;
her biri kendi yazılmayan sorusunu farklı bir yönde yanıtladı — kimi kopyaladı (dizi,
yapı), kimi paylaştı (dilim, sözlük, işaretçi), kimi hiç yazılamadı (dizgi). Altı dersin
ortak sorusu hep aynıydı: bir değeri bir yere aktardığında ne kopyalanır, ne paylaşılır?
Bir dizginin ne olduğunu dilin tip sistemi söylüyor: bir bayt dizisi, belirli bir
kodlamayla, değiştirilemez bir değer. Ama bir dizginin **ne anlama geldiğini** — kod
noktalarının hangi görünen işareti oluşturduğunu, bir adın ne temsil ettiğini — tip
sistemi söylemiyor.

Sıradaki konu aynı soruyu kaynağın **dışına** taşıyor. Şimdiye kadar ölçülen her şey bir
tek dosyanın, bir tek paketin içindeydi — bütün örnekler `package main` içinde, tek bir
dizinde durdu; hiçbir ölçüm bir adın başka bir dizindeki koddan görünüp görünmeyeceğini
sormadı. Ama gerçek bir program tek bir dosyadan ibaret değil: birden çok dosyaya,
birden çok pakete, birden çok bağımlılığa yayılıyor. Bir adın başka bir dosyadan
görülüp görülemeyeceğini, bir bağımlılığın hangi sürümünün seçileceğini ve yazılmamış bir
biçim kuralının nasıl uygulanacağını kim belirliyor? Bu sorular artık dilin tip
sisteminin değil, paket ve modül düzeneğinin sorularıdır.

## Paketler ve Araçlar

Dışa aktarmanın tek bir harfe bağlanması ve paket düzeninin görünürlükte karşılığı, modül dosyasının yazılmayan sürümü seçmesi, dilin kendi komut ailesinin yazılmayan kuralları uygulaması, kaynaktan belge üretiminin dayandığı yazım sözleşmeleri.

### Paket Düzeni ve Görünürlük

Source: https://academia.sh/tr/kurslar/go-temelleri/paket-duzeni-ve-gorunurluk

Dışa aktarımın bir paketten diğerine görünür olup olmadığını tek bir harfin büyüklüğüne bağladığı, derleyici program içinden çağrılarak dört denemeyle gösterilir: büyük harfli bir işlev ve yalnız dışa aktarılan alanı kurulan bir yapı derlenir; küçük harfli işlev ve yapının dışa aktarılmayan alanı başka paketten okunmaya çalışıldığında derlenmez. Sınırlayıcı ölçüm, aynı paketin iki ayrı dosyasının birbirinin küçük harfli adını sorunsuz paylaştığını gösterir — görünürlüğün sınırı dosya değil pakettir.

Önceki dersin kapanışı bir dizginin ne olduğunu dilin tip sisteminin söylediğini ama ne
anlama geldiğini söylemediğini kurdu. Bu ders aynı soruyu kaynağın dışına taşıyor: bir
adın görünürlüğünü, bir sürümün seçilmesini ve bir kuralın uygulanmasını kim belirliyor?
İlk durak görünürlük. Go'da bir tanımlayıcıyı başka bir pakete açık kılmak için yazılacak
bir anahtar sözcük yoktur — `public`, `private` ya da benzeri bir bildirim yoktur. Yazılan
tek şey addır ve adın **ilk harfinin büyük ya da küçük olması** bütün kararı verir. Bu
dersin sorusu kursun genel sorusunun aynısı: bunu yazmadığında, yani harfi büyük
yazmadığında, ne oluyor?

Bu sorunun kursun genel çerçevesindeki yeri biraz farklı. Önceki konuda "yazılmayan" şey hep bir
*değerdi* — ilk değeri verilmeyen bir değişken, kullanılmayan bir dönüş. Burada yazılmayan şey bir
**erişim kararı**: bir adı büyük harfle yazmamak. Yanıt da konuma bağlı hâle geliyor — aynı küçük
harfli ad, kendi paketinin içinde tanımlı bir biçimde çalışıyor, ama paketin dışından
çağrıldığında reddediliyor. Kursun geri kalanında tek bir sınıf tek bir sonuca karşılık gelirken,
bu derste aynı yazılmamışlığın karşılığı **nereden bakıldığına** göre değişiyor; ölçüm de bu
yüzden aynı adı iki farklı konumdan çağırarak kuruluyor.

#### Dışa Aktarmanın Kuralı: Bir Harf

**PK1.** Go, görünürlük için ayrı bir bildirim sözdizimi taşımaz; bir tanımlayıcının paket
dışından görünür olup olmadığı yalnız adın ilk harfinin büyük (**dışa aktarılan**) ya da
küçük (**dışa aktarılmayan**) olmasıyla belirlenir. **PK2.** Bu kural tek bir tanımlayıcı
türüne özgü değildir — işlev, tip, değişken, sabit ve bir yapının alanı aynı harfe bakan
aynı kuraldan geçer.

Bu, önceki derste kurulan sıfır değer kuralına benzer bir tasarım kararı: dil, yazılmayan
bir şeyin karşılığını **kendisi** belirliyor. Aradaki fark yönde: sıfır değer, yazılmayan
bir *değeri* dolduruyordu; dışa aktarma kuralı, yazılmayan bir *erişim bildirimini*
dolduruyor — ve harfin büyüklüğü dışında hiçbir şey yazılmadığında derleyici bunu daima
"dışa aktarılmıyor" olarak okur. Kararın kendisi bir seçim değil, adın yazılış biçiminin
zorunlu bir sonucu.

Bu tasarımın gerekçesi bir kısayoldan ibaret değil. Ayrı bir görünürlük bildirimi
(`public`/`private` gibi) her tanımlayıcı için ikinci bir karar ister: adı seç, sonra
görünürlüğü seç. Go bu ikinci kararı adın kendisine gömerek kaldırıyor — bir tanımlayıcının
görünürlüğünü değiştirmek, adını değiştirmek demektir ve bu ikisi hiçbir zaman
birbirinden ayrışamaz. Kararın bir sonucu da şu: görünürlük bir çalışma zamanı denetimi
değil, tamamen derleme anında çözülen bir sözdizim kuralı. Aşağıdaki ölçüm bunu tam olarak
bu yüzden ikinci bir çalıştırma yapmadan, yalnız derleyiciyi çağırarak sınıyor — görünürlük
ihlali bir istisna ya da hata değeri olarak değil, derlemenin kendisinin durması olarak
ortaya çıkıyor.

#### Bir Harfin Kapsadığı Alan

Aynı kural, üç dersten beri kurulan başka tanımlayıcı türlerine de sessizce uygulanıyor.
Önceki konuda `iota` ile sıralanan sabitler büyük harfle yazılırsa dışa aktarılır, küçük
harfle yazılırsa yalnız kendi paketinde kalır — sabitin **değeri** için ayrı bir kural
yoktur, yalnız adı için aynı kural işler. Bir yapıya bağlı bir yöntemin adı da aynı
sınavdan geçer: alıcısı (bileşik tipler konusunda kurulan kavram) dışa aktarılan bir tip
olsa bile, yöntemin kendi adı küçük harfle başlıyorsa o yöntem yalnız kendi paketinden
çağrılabilir. Bir paketin dışarıya sunduğu şey bu yüzden tek tek adlara bakılarak
okunabilir: paketin kaynağını tarayan biri, hangi işlevin, tipin, sabitin ya da yöntemin
dışarıdan erişilebilir olduğunu görmek için ayrı bir bildirim listesi aramaz, yalnız ilk
harflere bakar. Bu okunabilirlik bir yan etki değil, kuralın asıl amacı: bir paketin genel
yüzeyi, kaynağın kendisinden başka hiçbir belgeye bakmadan çıkarılabilir bir bilgi hâline
geliyor.

#### Derleyiciyi İki Paketli Bir Kurguyla Çağırmak

Kursun ilk dersinden beri süren yordam burada genişliyor: derleyici, kaynağı gerçekten
derleyerek sınanıyor, hata metni hiç okunmuyor, yalnız derlendi/derlenmedi sonucu alınıyor.
O dersteki kurgu tek bir paketi sınıyordu; bu ders **iki paketli** bir kurgu kuruyor, çünkü
görünürlüğün asıl sınandığı yer paket sınırının kendisi. Aşağıdaki yordamlar bir kütüphane
paketini ayrı bir alt dizine, onu çağıran tarafı modülün köküne yazıyor ve ikisini birlikte
derliyor.

```go
// araclar/araclar.go — derleyiciyi program icinden cagiran ortak yordamlar
package araclar

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

// IkiPaketDerlenirMi kutuphaneKaynagi'ni "sinama/kutuphane" alt paketine, anaKaynagi'ni
// gecici modulun kok dizinine yazip derler; iki paket arasindaki gorunurluk boylece
// gercek bir derleyici cagrisiyla sinanir. Hata metni okunmaz, yalniz basari donuyor.
func IkiPaketDerlenirMi(kutuphaneKaynagi, anaKaynagi 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.MkdirAll(filepath.Join(dizin, "kutuphane"), 0o755)
	os.WriteFile(filepath.Join(dizin, "kutuphane", "kutuphane.go"), []byte(kutuphaneKaynagi), 0o644)
	os.WriteFile(filepath.Join(dizin, "main.go"), []byte(anaKaynagi), 0o644)
	return calistir(dizin)
}

// AyniPaketIkiDosyaDerlenirMi ayni paketin iki dosyasini (biri tanimlayan, biri okuyan
// tarafi) tek bir gecici modulun kok dizinine yazip derler.
func AyniPaketIkiDosyaDerlenirMi(dosyaA, dosyaB 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, "a.go"), []byte(dosyaA), 0o644)
	os.WriteFile(filepath.Join(dizin, "b.go"), []byte(dosyaB), 0o644)
	return calistir(dizin)
}

func calistir(dizin string) bool {
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}
```

`IkiPaketDerlenirMi`, bir kütüphane kaynağını `kutuphane` adlı bir alt pakete, onu çağıran
kaynağı da modülün köküne yazıyor; ikisi arasındaki tek bağ `import` satırı. `derleyici`
(kod bloğunda yalnız `exec.Command("go", "build", ...)` çağrısının argümanı olarak geçiyor)
her seferinde tamamen yeni, boş bir dizine karşı çalışıyor — önceki denemenin kalıntısı bir
sonraki denemeyi etkilemiyor. Her deneme kendi geçici modülünü kuruyor çünkü aksi hâlde bir
denemenin bıraktığı dosya bir sonrakinin sonucunu bulandırabilir; taze bir dizin, ölçülen
şeyin yalnız o an yazılan iki kaynak olduğunu garanti ediyor. `AyniPaketIkiDosyaDerlenirMi`
ise farklı bir soru soruyor: iki dosya *aynı* pakete aitse ne oluyor? Bu iki yordam dışında
`araclar` paketi hiçbir şey bilmiyor — hangi adın "kütüphane", hangisinin "ana" olduğu,
hangi harfin büyük yazıldığı bu paketin dışında, çağıran tarafın kurduğu kaynak metinlerde
kalıyor. `GOTOOLCHAIN=local` ortam değişkeni her iki yordamda da veriliyor; iç derleme
çağrısının kendi araç zincirini ağdan indirmeye çalışmaması, yalnız zaten kurulu olanı
kullanması için.

#### Beş Deneme

Aşağıdaki ölçüm aynı işlevi ve aynı yapıyı birden çok biçimde yazıp iki paket arasında
sınıyor: bir işlev adı, bir yapı literalinin iki yazılışı ve bir alan okuması.

```go
// main.go — disa aktarimin kurali ve paket siniri dosya siniri degildir
package main

import (
	"fmt"

	"ders/araclar"
)

func goster(soru string, derlendiMi bool) {
	fmt.Printf("%-46s %v\n", soru, derlendiMi)
}

func main() {
	kutuphaneBuyuk := "package kutuphane\n\nfunc Topla(a, b int) int { return a + b }\n"
	anaBuyuk := "package main\n\nimport \"sinama/kutuphane\"\n\nfunc main() { _ = kutuphane.Topla(2, 3) }\n"
	goster("Topla (buyuk harf), baska paketten cagriliyor", araclar.IkiPaketDerlenirMi(kutuphaneBuyuk, anaBuyuk))

	kutuphaneKucuk := "package kutuphane\n\nfunc topla(a, b int) int { return a + b }\n"
	anaKucuk := "package main\n\nimport \"sinama/kutuphane\"\n\nfunc main() { _ = kutuphane.topla(2, 3) }\n"
	goster("topla (kucuk harf), baska paketten cagriliyor", araclar.IkiPaketDerlenirMi(kutuphaneKucuk, anaKucuk))

	kutuphaneYapi := "package kutuphane\n\ntype Kayit struct {\n\tAd    string\n\tgizli int\n}\n"
	anaYapiKur := "package main\n\nimport \"sinama/kutuphane\"\n\nfunc main() { _ = kutuphane.Kayit{Ad: \"x\"} }\n"
	goster("Kayit{Ad: \"x\"}, adli alanla kuruluyor", araclar.IkiPaketDerlenirMi(kutuphaneYapi, anaYapiKur))

	anaYapiPozisyonel := "package main\n\nimport \"sinama/kutuphane\"\n\nfunc main() { _ = kutuphane.Kayit{\"x\", 0} }\n"
	goster("Kayit{\"x\", 0}, pozisyonel olarak kuruluyor", araclar.IkiPaketDerlenirMi(kutuphaneYapi, anaYapiPozisyonel))

	anaYapiOku := "package main\n\nimport \"sinama/kutuphane\"\n\nfunc main() { k := kutuphane.Kayit{Ad: \"x\"}; _ = k.gizli }\n"
	goster("k.gizli, baska paketten okunuyor", araclar.IkiPaketDerlenirMi(kutuphaneYapi, anaYapiOku))

	fmt.Println()
	dosyaA := "package kutuphane\n\nvar deger = 42\n"
	dosyaB := "package kutuphane\n\nfunc Oku() int { return deger }\n"
	goster("deger (kucuk harf), ayni paketin baska dosyasindan okunuyor", araclar.AyniPaketIkiDosyaDerlenirMi(dosyaA, dosyaB))
}
```

```
Topla (buyuk harf), baska paketten cagriliyor  true
topla (kucuk harf), baska paketten cagriliyor  false
Kayit{Ad: "x"}, adli alanla kuruluyor          true
Kayit{"x", 0}, pozisyonel olarak kuruluyor     false
k.gizli, baska paketten okunuyor               false

deger (kucuk harf), ayni paketin baska dosyasindan okunuyor true
```

**PK3.** Beş çapraz paket denemesinden ikisi derleniyor, üçü derlenmiyor; ayrımı belirleyen
şey her seferinde denenen adın ilk harfi ya da yazılışın örtük olarak o harfe dokunup
dokunmadığı. **PK4.** `Kayit{Ad: "x"}` — adlı alanla kurulan literal — derleniyor: `Kayit`
yapısının dışa aktarılmayan bir `gizli` alanı olması, yapının kendisinin ya da yalnız dışa
aktarılan alanlarının kullanılmasını engellemiyor. **PK5.** Aynı yapı `Kayit{"x", 0}` ile,
yani alan adı yazılmadan sırayla kurulmaya çalışıldığında derlenmiyor; bu yazılış, ikinci
konuma `gizli` alanına örtük bir değer atıyor ve derleyici bunu paket dışından yapılan bir
dışa aktarılmayan alan ataması olarak reddediyor. **PK6.** `k.gizli` okuması da
derlenmiyor — aynı yapının aynı örneği, farklı bir alan istendiğinde reddediliyor.

Üç ve dördüncü satır (adlı literal, pozisyonel literal) birlikte okunduğunda dışa aktarma
kuralının **alan başına** işlediği ve **yazılış biçimine duyarlı** olduğu görülüyor: bir yapı bir
bütün olarak dışa aktarılsa bile (`Kayit` büyük harfle başladığı için zaten dışa aktarılıyor
burada), her alanı kendi harfine göre ayrı ayrı değerlendiriliyor. Adlı literal yalnız `Ad`
alanını yazdığını söylüyor, `gizli` alanına ise hiç dokunmuyor — derleyici bunu kabul ediyor ve
`gizli`ye kendi sıfır değerini bırakıyor. Pozisyonel literal ise **bütün** alanlara sırayla değer
atamayı gerektiriyor; ikinci konum `gizli` alanına düşüyor ve bu, kaynak metinde yazılmasa bile,
derleyicinin gözünde dışa aktarılmayan bir alana paket dışından yazmaya eşdeğer. Kaydın bir
alanını okumak (`k.gizli`) da aynı ailede: adı paket dışından anmanın kendisi, okuma ya da örtük
yazma fark etmeksizin, reddediliyor. Derleyici üç durumda da hata metnini basmıyor — yalnız
`derlendi`/`derlenmedi` okunuyor — ama üçünün ortak nedeni aynı: paket dışından görünmeyen bir adı
kaynak metinde anmak.

#### Sınır Paket Sınırıdır, Dosya Sınırı Değildir

**PK7.** Aynı paketin iki ayrı kaynak dosyası, aralarında hiçbir `import` olmadan,
birbirinin dışa aktarılmayan adını doğrudan paylaşıyor: `a.go` küçük harfli `deger`
değişkenini tanımlıyor, `b.go` onu doğrudan okuyor ve ikisi de derleniyor.

Bu, dersin sınırlayıcı ölçümü. Görünürlük kuralı harfin büyüklüğüne bakıyor demiştik, ama
bu kural **neye göre** uygulanıyor sorusunun yanıtı "dosyaya göre" değil. Yukarıdaki
ölçümde `a.go` ve `b.go` iki ayrı dosya, iki ayrı derleme biriminde yazılmış gibi
görünüyor — ama ikisi de `package kutuphane` bildirimini taşıyor ve bu tek satır ikisini
derleyicinin gözünde **tek bir birleşik ad alanına** sokuyor. `deger` küçük harfle
başladığı için paket dışından hiç görünmez, ama aynı paketin herhangi bir dosyasından,
hiçbir `import` yazmadan, doğrudan görünür.

Bu ayrım sık gözden kaçıyor çünkü birçok dilde derleme birimi dosyanın kendisidir ve bir dosyanın
gördüğü ad, o dosyanın kendi bildirimleriyle sınırlıdır. Go'da derleme birimi **paket**tir; bir
paketin kaç dosyaya bölündüğü yalnız düzenleme kolaylığı için verilmiş bir karardır ve görünürlüğü
hiç değiştirmez. Bir paketi on dosyaya bölmek ile tek dosyada tutmak, o paketin dışa aktardığı ve
dışa aktarmadığı adlar açısından **birebir aynı** sonucu verir — fark yalnız kaynağı okuyan
insanın hangi dosyaya bakacağıdır, derleyicinin gördüğü ad kümesi değil.

Bunun pratik bir sonucu var: bir paketi birden çok dosyaya bölmek yalnız okunabilirlik
içindir, hiçbir erişim kaybına yol açmaz. Gerçek paketler genellikle bu yüzden konuya göre
bölünür — bir dosya tipleri taşır, bir başkası o tiplerin üzerindeki işlevleri, bir
üçüncüsü hata değerlerini — ve bu dosyalar birbirinin dışa aktarılmayan adlarını hiçbir
ekstra bildirim yazmadan paylaşır. Ölçüm bunu tam tersinden de doğruluyor: `a.go` ile
`b.go` arasında `deger`i taşıyan hiçbir köprü kod yok, çünkü köprüye gerek yok — ikisi
`package kutuphane` satırını paylaştığı an aynı ad alanına giriyorlar. Eğer görünürlüğün
sınırı dosya olsaydı, bir paketi birden çok dosyaya bölmenin bedeli her seferinde yeni bir
"iç API" kurmak olurdu; Go'da bu bedel hiç yok, çünkü sınır zaten dosyanın değil paketin
sınırı.

Bu, kapsam (M01/K02 Programlama Temelleri kursunda kurulan kavram) ile aynı şey değildir.
Kapsam bir adın *nerede* görülebileceğini bir blok, işlev ya da paket düzeyinde belirler;
dışa aktarma kuralı ise paket düzeyinde zaten görünür olan bir adın, o paketin **dışından**
görülüp görülemeyeceğine karar verir. `deger` paketin her dosyasında aynı paket kapsamına
aittir; dışa aktarma kuralı bu kapsamın kendisini değiştirmiyor, yalnız kapsamın paket
sınırını **aşıp aşamayacağını** belirliyor.

#### Özet

- Go'da görünürlük için ayrı bir bildirim sözdizimi yoktur; bir adın dışa aktarılıp
  aktarılmadığı yalnız ilk harfinin büyük ya da küçük olmasıyla belirlenir.
- Bu kural işlev, tip, değişken, sabit ve yapı alanı için aynı biçimde işler; bir yapı bir
  bütün olarak dışa aktarılsa bile her alanı kendi harfine göre ayrı değerlendirilir.
- Beş çapraz paket denemesinden ikisi (büyük harfli işlev, yalnız dışa aktarılan alanı
  kuran yapı literali) derlenir; üçü (küçük harfli işlev, pozisyonel literalin örtük
  gizli-alan ataması, dışa aktarılmayan alanı okuma) derlenmez.
- Sınırlayıcı ölçüm: derleme birimi dosya değil pakettir; aynı paketin iki ayrı dosyası
  hiçbir `import` olmadan birbirinin küçük harfli adını paylaşır.
- Derleyici bu derste de program içinden çağrıldı ve yalnız derlendi/derlenmedi sonucu
  okundu; hata metni hiçbir ölçümde basılmadı.

#### Sonraki Adım

Bu derste sınırın nerede durduğu — pakette, dosyada değil — koşturularak ölçüldü ve
görünürlüğün tamamen derleme anında, tek bir harfe bakılarak çözüldüğü altı ayrı denemeyle
doğrulandı. Ama bir paket kendi başına yeterli değildir; çoğu zaman başka paketlere
bağımlıdır ve modülün gördüğü bu paketler her zaman aynı modülün içinde durmaz — ayrı bir
modülden gelirler ve bir **sürüm** taşırlar. O sürümün kaynakta hangi satırda, hangi
biçimde yazılacağı çoğu zaman hiç belirtilmez; bazen doğrudan yazılır, bazen bir
bağımlılığın bağımlılığından türetilir. Sıradaki ders bu soruyu ele alıyor: bir
bağımlılığın sürümü yazılmadığında modül dosyası onun yerine neyi seçiyor, bu seçim hangi
kurala göre yapılıyor ve seçilen sürüm neden her zaman mevcut en yeni sürüm olmuyor?

### Modüller ve Bağımlılıklar

Source: https://academia.sh/tr/kurslar/go-temelleri/moduller-ve-bagimliliklar

Bir bağımlılığın sürümü kaynakta çoğu zaman hiç yazılmaz; altı istekten üçü ana modülün kendi kaynağında yazılı, üçü bir bağımlılığın kendi bağımlılığından türetilmiştir. Modül dosyasının yazılmayan sürümü doldurma kuralı — en düşük sürüm seçimi — kaynak içinde modellenerek koşturulur; hiçbir ağ çağrısı yapılmaz. Sınırlayıcı ölçüm, seçilen sürümün bir modülün kataloğundaki en yeni sürüm olmadığını, yalnız yazılan isteklerin en büyüğü olduğunu gösterir.

Önceki ders bir paketin sınırının pakette durduğunu, dosyada durmadığını gösterdi. Bu ders
aynı soruyu bir düzey yukarı taşıyor: bir paket başka bir pakete bağımlı olduğunda, o
bağımlılığın **hangi sürümü** kastettiği kaynakta nerede yazılı? Çoğu zaman hiçbir yerde.
Bir modül dosyası doğrudan bağımlı olduğu birkaç modülün sürümünü yazar; o modüllerin
kendi bağımlılıklarının sürümü ise hiç yazılmaz, modüllerin kendi dosyalarından türer.
Bu dersin sorusu kursun genel sorusunun aynısı: sürüm yazılmadığında modül dosyası onun
yerine neyi koyuyor?

Bir bağımlılık grafiğinin sürüm çözmesi gereken bir sorunu var: aynı modül, grafikte
birden fazla yoldan isteniyorsa, o modülün **hangi** sürümüyle derleneceği kaynağın hiçbir
tek satırında yazmıyor. İki ayrı bağımlılık aynı üçüncü modülü farklı en düşük sürümlerle
istediğinde, ana modül ikisi arasında seçim yapmak zorunda kalıyor — ama bu seçim ana
modülün kaynağında görünmüyor, çünkü ana modül o üçüncü modülü hiç doğrudan anmıyor.
Bu durumun adı bağımlılık grafiklerinde sık geçer: iki ayrı yol aynı üçüncü noktaya
varıyor ve o noktada iki farklı istek çarpışıyor. Çarpışma bir hata değil — iki istek de
"en az şu sürüm" diyor, ikisi de yalnız bir alt sınır çiziyor, üst sınır çizmiyor. Seçimin
görevi bu iki alt sınırı **tek bir sürümde** birleştirmek ve birleştirilen sürümün her iki
isteği de karşıladığından emin olmak. Aşağıdaki ölçüm bu durumu en küçük hâliyle kuruyor ve
seçim algoritmasını gerçekten çalıştırarak sonucu okuyor.

#### Altı İstek, İki Kaynak

Aşağıdaki ölçüm gerçek bir modül kaydına hiç bağlanmıyor; bağımlılık grafiği kaynak içinde
elle kuruluyor ve **modellendiği açıkça burada yazılıyor**. Dört modül var: `kutuphaneA`,
`kutuphaneB`, `kutuphaneC`, `kutuphaneD`. Ana modül `kutuphaneA`, `kutuphaneB` ve
`kutuphaneD`'yi doğrudan istiyor — üçünün sürümü de kaynakta yazılı. `kutuphaneA`,
`kutuphaneC`'nin bir sürümünü istiyor; `kutuphaneB` da `kutuphaneC`'nin başka bir sürümünü.
`kutuphaneC` da kendi payına `kutuphaneD`'nin bir sürümünü istiyor. Bu üç istek ana
modülün kaynağında hiç yazılı değil — ana modülün derlemesi sırasında, bağımlı olunan
modüllerin kendi dosyalarından **türetiliyor**.

```go
// main.go — en dusuk surum secimi, bagimlilik grafigi kaynak icinde modelleniyor (ag cagrisi yok)
package main

import "fmt"

// Surum uc parcali bir surum numarasini tasir; bu ders gercek bir modul kaydina
// baglanmiyor, grafigin tamami asagida elle kuruluyor.
type Surum struct {
	Ana, Ikincil, Yama int
}

func (s Surum) metin() string {
	return fmt.Sprintf("%d.%d.%d", s.Ana, s.Ikincil, s.Yama)
}

// buyukMu s'nin digerinden daha yeni bir surum oldugunu soyler.
func (s Surum) buyukMu(diger Surum) bool {
	if s.Ana != diger.Ana {
		return s.Ana > diger.Ana
	}
	if s.Ikincil != diger.Ikincil {
		return s.Ikincil > diger.Ikincil
	}
	return s.Yama > diger.Yama
}

// Istek, bir modulun bagimliliginin istedigi en dusuk surumu tasir. Yazili true ise
// istek dogrudan ana modulun kendi kaynaginda duruyor; degilse baska bir bagimliligin
// kendi modul dosyasindan turedi.
type Istek struct {
	Modul   string
	Kimden  string
	Istenen Surum
	Yazili  bool
}

func main() {
	istekler := []Istek{
		{Modul: "kutuphaneA", Kimden: "ana modul", Istenen: Surum{1, 2, 0}, Yazili: true},
		{Modul: "kutuphaneB", Kimden: "ana modul", Istenen: Surum{1, 1, 0}, Yazili: true},
		{Modul: "kutuphaneD", Kimden: "ana modul", Istenen: Surum{2, 1, 0}, Yazili: true},
		{Modul: "kutuphaneC", Kimden: "kutuphaneA", Istenen: Surum{1, 4, 0}, Yazili: false},
		{Modul: "kutuphaneC", Kimden: "kutuphaneB", Istenen: Surum{1, 6, 0}, Yazili: false},
		{Modul: "kutuphaneD", Kimden: "kutuphaneC", Istenen: Surum{2, 0, 0}, Yazili: false},
	}

	fmt.Println("-- alti istek, kaynagi yazili mi turetilmis mi --")
	yaziliSayisi, turetilenSayisi := 0, 0
	for _, ist := range istekler {
		kaynak := "turetildi"
		if ist.Yazili {
			kaynak = "yazili"
			yaziliSayisi++
		} else {
			turetilenSayisi++
		}
		fmt.Printf("%-12s <- %-12s %-9s istenen=%s\n", ist.Modul, ist.Kimden, kaynak, ist.Istenen.metin())
	}
	fmt.Printf("toplam: %d yazili, %d turetildi\n", yaziliSayisi, turetilenSayisi)

	fmt.Println()
	fmt.Println("-- secilen surum: her modulun tum isteklerinin en buyugu --")
	siraliModuller := []string{"kutuphaneA", "kutuphaneB", "kutuphaneC", "kutuphaneD"}
	secilen := map[string]Surum{}
	for _, ist := range istekler {
		mevcut, varMi := secilen[ist.Modul]
		if !varMi || ist.Istenen.buyukMu(mevcut) {
			secilen[ist.Modul] = ist.Istenen
		}
	}
	for _, ad := range siraliModuller {
		fmt.Printf("%-12s secilen=%s\n", ad, secilen[ad].metin())
	}

	fmt.Println()
	fmt.Println("-- sinirlayici olcum: secim en yeniyi degil, yazilan en dusugu aliyor --")
	kataloktakiEnYeniC := Surum{1, 9, 0}
	fmt.Printf("kutuphaneC icin katalogtaki en yeni surum: %s\n", kataloktakiEnYeniC.metin())
	fmt.Printf("secilen surum: %s\n", secilen["kutuphaneC"].metin())
	fmt.Printf("secilen, katalogtaki en yeniyle ayni mi: %v\n", secilen["kutuphaneC"] == kataloktakiEnYeniC)
}
```

```
-- alti istek, kaynagi yazili mi turetilmis mi --
kutuphaneA   <- ana modul    yazili    istenen=1.2.0
kutuphaneB   <- ana modul    yazili    istenen=1.1.0
kutuphaneD   <- ana modul    yazili    istenen=2.1.0
kutuphaneC   <- kutuphaneA   turetildi istenen=1.4.0
kutuphaneC   <- kutuphaneB   turetildi istenen=1.6.0
kutuphaneD   <- kutuphaneC   turetildi istenen=2.0.0
toplam: 3 yazili, 3 turetildi

-- secilen surum: her modulun tum isteklerinin en buyugu --
kutuphaneA   secilen=1.2.0
kutuphaneB   secilen=1.1.0
kutuphaneC   secilen=1.6.0
kutuphaneD   secilen=2.1.0

-- sinirlayici olcum: secim en yeniyi degil, yazilan en dusugu aliyor --
kutuphaneC icin katalogtaki en yeni surum: 1.9.0
secilen surum: 1.6.0
secilen, katalogtaki en yeniyle ayni mi: false
```

**PK8.** Altı istekten üçü kaynakta yazılı, üçü türetilmiştir; hangi üçünün yazılı olduğu,
o modülün ana modülün **doğrudan** bağımlılığı olup olmamasına bağlıdır. **PK9.**
`kutuphaneC`'nin iki ayrı isteği var — biri `kutuphaneA`'dan, biri `kutuphaneB`'den — ve
ikisi de kaynakta hiç görünmeden, yalnız bu iki modülün kendi dosyalarından türüyor.

#### En Düşük Sürüm Seçimi

**PK10.** Bir modülün seçilen sürümü, o modül için yazılan bütün isteklerin en büyüğüdür;
`kutuphaneC` için hem `1.4.0` hem `1.6.0` istendiğinde seçilen `1.6.0` oluyor ve `kutuphaneD` için
hem `2.0.0` hem `2.1.0` istendiğinde seçilen `2.1.0` oluyor. Kuralın adı **en düşük sürüm
seçimi**, ama seçilen sayı isteklerin en küçüğü değil en büyüğü — çelişki gibi görünen bu
adlandırma, kuralın neyi garanti ettiğine bakıldığında çözülüyor. Kural şunu söylüyor: seçilecek
sürüm, **hiçbir isteği ihlal etmeyen en düşük sürüm** olsun. `1.4.0` isteyen bir bağımlılık
`1.6.0` ile de çalışır (sürümler geriye uyumlu kabul edilir), ama `1.6.0` isteyen bir bağımlılık
`1.4.0` ile çalışacağının garantisini vermez. Bu yüzden bütün isteklerin **hepsini** karşılayan en
düşük sürüm, isteklerin en büyüğüdür — "en düşük" sözcüğü tek bir isteğe değil, bütün istekleri
aynı anda karşılayan noktaya işaret ediyor.

Bu, geriye uyumluluğun bir varsayım olarak alındığı bir hesap; kural kendisi bunu
denetlemiyor, yalnız kabul ediyor. Yukarıdaki `buyukMu` karşılaştırması üç sayıyı sırayla
kıyaslıyor ve hiçbir yerde iki sürümün gerçekten uyumlu olup olmadığını sınamıyor —
uyumluluk, sürüm numarasının kendisine bağlanmış bir söz olarak kabul ediliyor, ayrıca
doğrulanmıyor. Seçim algoritması bu sözün tutulduğu bir dünyada doğru sonuç veriyor;
söz tutulmadığında (yeni sürüm eskisiyle aynı arayüzü sunmadığında) algoritma hâlâ aynı
sayıyı seçiyor, ama seçilen sürümün gerçekten çalışıp çalışmadığı ayrı bir sorudur — bu
sorunun yanıtı modül dosyasında değil, kaynağın kendisinin derlenip derlenmediğinde
bulunuyor.

Bu hesap `kutuphaneD` için iki katmanlı bir zincir üzerinden de çalışıyor: `kutuphaneD`
doğrudan ana modülden bir istek alıyor (`2.1.0`, yazılı) ve dolaylı olarak
`kutuphaneC` üzerinden bir istek daha alıyor (`2.0.0`, türetilmiş). Seçim bu ikisi arasında
hiçbir ayrım yapmıyor — istek nereden geldiğine değil, hangi sayıyı taşıdığına göre
karşılaştırılıyor ve büyük olan (`2.1.0`) kazanıyor. Bir isteğin yazılı ya da türetilmiş
olması, seçim algoritmasının **girdisi** değil; yalnız bu dersin ölçtüğü, isteğin kaynağını
sınıflandıran ayrı bir gözlem.

Bu ayrımın önemi isteklerin sayısı arttıkça büyüyor. Yukarıdaki ölçümde dört modül ve altı
istek var; gerçek bir bağımlılık grafiğinde bu sayı yüzlerle ölçülebilir ve aynı modülün
onlarca farklı yoldan istenmesi sıradan bir durumdur. Seçim kuralı bu ölçekte de
değişmiyor: her modül için, o modüle giden bütün yolların istediği sürümlerin en büyüğü
alınıyor. Algoritmanın kendisi grafiğin derinliğine duyarsız — `kutuphaneD`'ye giden istek
`kutuphaneC` üzerinden iki basamak uzaklıktan gelse de, ana modülden doğrudan gelse de,
karşılaştırma aynı iki sayıyı yan yana koyup büyüğünü seçmekten ibaret. Karmaşıklık
grafiğin büyüklüğünde duruyor, kuralın kendisinde değil.

#### Sınırlayıcı Ölçüm: En Yeni Değil, Yazılan En Düşük

Yukarıdaki koşum bir katalog değeri daha taşıyor: `kutuphaneC` için "mevcut en yeni sürüm"
`1.9.0` olarak veriliyor, ama seçilen sürüm `1.6.0`. **PK11.** Seçim bir modülün elinde
bulunan en yeni sürümü hiç sormuyor; yalnız gerçekten yazılan (ya da türetilen) isteklere
bakıyor ve onların en büyüğünü alıyor. Bu, en düşük sürüm seçiminin en çok yanlış anlaşılan
tarafı: kural bir bağımlılığı **güncel tutmuyor**, yalnız kaynakta bir yerde (doğrudan ya
da dolaylı) istenmiş olanı karşılıyor. `kutuphaneC`'nin `1.9.0` sürümü var olabilir, ama
hiçbir istek onu istemediği sürece seçilen sürüme hiç girmiyor.

Bunun bilinçli bir tercih olduğu, neyi güvence altına aldığına bakılınca görülüyor: bir
bağımlılık grafiğinin sonucu, o grafiği kimin, ne zaman derlediğinden bağımsız olsun
isteniyor. Eğer seçim "her zaman en yeniyi al" olsaydı, aynı kaynak metin farklı
zamanlarda derlendiğinde farklı bir bağımlılık kümesiyle derlenebilirdi — üstelik
kaynağın kendisi hiç değişmeden. En düşük sürüm seçimi bunun yerine yalnız kaynakta
(dolaylı olarak da olsa) yazılana bakıyor; aynı istek kümesi, ne zaman ve nerede
çalıştırılırsa çalıştırılsın, aynı sürüm kümesini seçiyor. Bedeli açık: bir bağımlılığın
yeni bir sürümünden yararlanmak için o sürümü **isteyen bir satırın** bir yerde
yazılması gerekiyor; hiçbir istek kendiliğinden yükselmiyor.

Bunun bir sonucu daha var: iki ayrı geliştirici, aynı bağımlılık grafiğini aynı anda,
birbirinden habersiz çalıştırsa bile aynı sürüm kümesine varır — çünkü seçim hiçbir dış
duruma (kataloğun o anki içeriğine, hangi sürümlerin "en yeni" sayıldığına) bakmıyor,
yalnız kaynakta yazılana bakıyor. Bir bağımlılığı yükseltmek istendiğinde bu yüzden ayrı
ve bilinçli bir adım gerekiyor: ya doğrudan istenen sürüm kaynakta değiştiriliyor, ya da
zincirdeki bir ara bağımlılığın kendi isteği yükseltiliyor. Her iki durumda da yükselme,
bir yerde yeni bir sayının **yazılmasıyla** oluyor; yazılmadığı sürece seçim hep aynı
sonuca dönüyor, çünkü hesabın kendisi belirlenimci.

#### Yazılı, Türetilmiş, Seçilmiş

Bir modül dosyasının gerçek kaynağında bu üç kavram üç ayrı satıra dağılır. Ana modülün
doğrudan istediği sürümler modül dosyasının kendi bildirim bloğunda, açıkça yazılı durur.
Yalnızca türetilmiş bir sürümü taşıyan modüller de aynı bloğa düşer, ama bu satırlar
"dolaylı" olarak işaretlenir — kaynağın hiçbir yerinde `import` edilmeyen, yalnız seçim
hesabının bir girdisi olan modülleri gösterirler. Seçilen sürüm ise bu iki kümenin
birleşiminden, yukarıdaki hesapla çıkar; modül dosyası bu üçünü ayrı ayrı taşıdığı için bir
modülün neden o sürümde durduğu — kendi isteği mi yoksa başka bir bağımlılığın isteği mi —
kaynaktan geriye doğru izlenebilir kalır.

Gerçek bir modül dosyasında bu bildirim bloğu satır satır okunabilir: her satır bir modül
adı ve o modül için seçilmiş sürümü taşır ve doğrudan istenmeyip yalnız türetilmiş olan
bir modülün satırının sonuna bunu belirten kısa bir not eklenir. Bu notun kendisi bir sürüm
bilgisi değil, bir **köken** bilgisidir — o modülün ana modülün kaynağında hiç
`import` edilmediğini, yalnız seçim hesabına başka bir modül üzerinden girdiğini söyler.
Yukarıdaki ölçümdeki `Yazili` alanı tam olarak bu notun karşılığı: `kutuphaneA`,
`kutuphaneB`, `kutuphaneD` için `Yazili` alanı `true` (bunlar ana modülün kendi kaynağında
`import` ediliyor), `kutuphaneC`'nin iki isteği için `false` (hiçbiri ana modülün
kaynağında hiç anılmıyor). Bildirim bloğunun kendisi seçim hesabından **sonra** yazılır —
önce bütün istekler toplanır, en düşük sürüm seçimi çalıştırılır, sonra yalnız sonuç
(seçilen sürüm) bloğa düşer. Ara adımlar — hangi isteğin hangi modülden geldiği — modül
dosyasının kendisinde hiç görünmez; bu bilgi yalnız seçim hesabı çalışırken bir an için
vardır, tıpkı yukarıdaki ölçümün `istekler` diliminde durduğu gibi.

Bu üç ayrımın (yazılı, türetilmiş, seçilmiş) pratik bir sonucu var: bir modül dosyasını
okuyan biri, bir bağımlılığın neden orada durduğunu iki farklı soruyla ayırt edebiliyor.
"Bu modülü ben mi istedim?" sorusunun yanıtı doğrudan/dolaylı ayrımında duruyor. "Hangi
sürümü aldık?" sorusunun yanıtı ise seçilen sürüm satırında duruyor — ama bu ikinci soru
kaynağın **hiçbir** satırından tek başına çıkmıyor, çünkü seçilen sürüm birden fazla
isteğin karşılaştırılmasının sonucu. Modül dosyası bu yüzden bir bildirim listesinden çok
bir **hesabın son hâli**: her satır, kaynakta görünmeyen bir karşılaştırmanın kazananını
taşıyor.

Bağımlılık sürümünün nereden geldiğini bildirim satırından okumak (**M06/K04 Modüller,
Araçlar ve Ekosistem**) ve bir paketin hangi sürümle yayımlanacağını belirlemek
(**M07/K05 Python Projeleri: Paketleme ve Test**) başka dillerin kendi düzeneğinde daha
önce kuruldu; bu ders o modeli tekrarlamıyor, yalnız Go'nun kendi seçim kuralını —
isteklerin en büyüğünü almayı — ölçüyor.

#### Özet

- Bir bağımlılık grafiğindeki istekler iki kaynaktan gelir: ana modülün kendi kaynağında
  yazılı olanlar ve bir bağımlılığın kendi modül dosyasından türeyenler.
- En düşük sürüm seçimi, bir modül için yazılan bütün isteklerin **en büyüğünü** seçer;
  bu, hiçbir isteği ihlal etmeyen en düşük ortak nokta olduğu için "en düşük" adını taşır.
- İsteğin yazılı ya da türetilmiş olması seçim hesabını etkilemez; yalnız hangi sayının
  taşındığı karşılaştırılır.
- Sınırlayıcı ölçüm: seçilen sürüm bir modülün kataloğundaki en yeni sürüm değildir,
  yalnız gerçekten istenmiş olanların en büyüğüdür — hiçbir istek kendiliğinden yükselmez.
- Bu ölçüm gerçek bir ağ çağrısı yapmadan, bağımlılık grafiğini kaynak içinde modelleyerek
  koşturuldu.

#### Sonraki Adım

Bu derste bir modülün sürümünü kimin seçtiği ölçüldü: seçimi ne bir kişi ne ayrı bir araç
belirliyor, isteklerin kendisi belirliyor — kaynakta yazılı olsun ya da bir bağımlılıktan
türesin, hesap ikisine de aynı kuralı uyguluyor. Bir modülün sürümü bu yüzden kaynağın
dışında, seçim algoritmasının bir çıktısı olarak duruyor. Ama bir modülün içindeki
kaynağın **biçimi** ve uyduğu **kalıp** için aynı sorunun yanıtı farklı bir yerde: onları
uygulayan bir kişi ya da bir kural kitabı değil, Go'nun kendi komut ailesi. Sıradaki ders
bu komutları program içinden çağırıp, yazılmayan bir biçim kuralının kaynağı nasıl
değiştirdiğini ve derleyicinin reddetmediği bir kalıbı hangi aracın yakaladığını ölçüyor.

### Go Komut Ailesi

Source: https://academia.sh/tr/kurslar/go-temelleri/go-komut-ailesi

Dilin kendi komut ailesi, yazılmayan iki kuralı program içinden çağrılarak uyguluyor. Bir kaynak biçimlendiriciden geçirildiğinde bayt bayt karşılaştırılıyor ve 18 satırlık kaynağın 7 satırının değiştiği sayılıyor; inceleyici ise derleyicinin reddetmediği bir kalıbı (bir yazdırma çağrısının biçim damgasıyla verilen değerin tipinin uyuşmaması) yakalıyor. Sınırlayıcı ölçüm, biçimlendirilmiş ve biçimlendirilmemiş iki kaynağın aynı çalıştırıldığında aynı çıktıyı verdiğini gösterir — biçim değişiyor, anlam değişmiyor.

Önceki derste bir modülün sürümünü kimin seçtiği ölçüldü: seçimi isteklerin kendisi
belirliyordu, ayrı bir karar mekanizması yoktu. Bu derste soru bir düzey daha somutlaşıyor:
bir kaynağın **biçimi** ve uyduğu **kalıp** için yazılmayan kuralları kim uyguluyor? Yanıt
bu kez bir kişi ya da bir sözleşme belgesi değil — dilin kendi dağıtımıyla gelen komut
ailesi. Kursun ilk dersinden beri derleyici bu ailenin bir üyesi olarak program içinden
çağrılıyordu; bu ders aynı aileden iki üyeyi daha, biçimlendirmeyi ve incelemeyi, aynı
yöntemle çağırıyor.

Kursun ilk üç konusu boyunca bu komut ailesinden yalnız bir üye çağrıldı: derleyici. Onun işi
dardı — kaynağın çalıştırılabilir bir programa dönüşüp dönüşmeyeceğine karar vermek. Ama bir
kaynak metnin çevresinde derlenip derlenmemesinin ötesinde iki soru daha duruyor: bu kaynak
**nasıl görünüyor** ve bu kaynak **hangi kalıplara** uyuyor? Derleyici ikisine de kayıtsız — bir
kaynağın girintisi, boşlukları, satır sonları neresi olursa olsun aynı programı üretiyor ve
derleyicinin sözdizim kuralları bir kalıbın "iyi" ya da "kötü" olduğuna hiç karar vermiyor; yalnız
kalıbın geçerli olup olmadığına bakıyor. Bu iki sorunun yanıtı bu yüzden derleyicinin dışında,
aynı komut ailesinin iki ayrı üyesinde duruyor.

#### Bir Kaynağı Biçimlendiriciden Geçirmek

**PK12.** Bir kaynağın biçimi — girinti, boşluk, hizalama — dilin sözdizim kuralları
tarafından zorunlu kılınmaz; aynı anlama gelen bir kaynak birçok farklı biçimde yazılabilir.
**PK13.** Buna rağmen biçimlendirici, kendisine verilen herhangi bir geçerli kaynağı, tek
bir kanonik biçime dönüştürür ve bu dönüşüm kaynağın **anlamını** hiç değiştirmez.

Aşağıdaki yordamlar, önceki derslerde kurulan derleyici çağırma deseninin aynısını
kullanıyor: kaynak geçici bir modüle yazılıyor, komut çağrılıyor, sonuç okunuyor. Fark
şurada — bu kez çağrılan komut kaynağı **derlemiyor**, kaynağın kendisini **değiştiriyor**.
Bu yüzden ölçüm de farklı bir şey okuyor: önceki derslerde tek bir soru vardı
(derlendi mi, derlenmedi mi); burada iki kaynak — biçimlendirmeden önceki ve sonraki —
yan yana tutulup karşılaştırılıyor. Her ölçüm yine kendi tamamen boş geçici modülünde
çalışıyor ve `GOTOOLCHAIN=local` ortam değişkeniyle çağrılıyor, tıpkı önceki iki derste
olduğu gibi — çağrılan komut ne bir ağ isteği göndersin ne de önceki bir denemenin
bıraktığı bir dosyayı görsün diye.

```go
// araclar/araclar.go — go komut ailesini program icinden cagiran ortak yordamlar
package araclar

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

func yeniModul(dizin string) {
	os.MkdirAll(dizin, 0o755)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
}

func ortam() []string {
	return append(os.Environ(), "GOTOOLCHAIN=local")
}

// Bicimlendir kaynagi gecici bir modulde bicimlendiricinin uzerinden gecirir; bicimlendirme
// oncesi ve sonrasi kaynagi, ayrica kac satirin degistigini dondurur.
func Bicimlendir(kaynak string) (oncesi, sonrasi string, degisenSatir int) {
	dizin, _ := os.MkdirTemp("", "sinama")
	defer os.RemoveAll(dizin)
	yeniModul(dizin)
	yol := filepath.Join(dizin, "main.go")
	os.WriteFile(yol, []byte(kaynak), 0o644)

	cmd := exec.Command("go", "fmt", "./...")
	cmd.Dir = dizin
	cmd.Env = ortam()
	cmd.Run()

	bayt, _ := os.ReadFile(yol)
	oncesi = kaynak
	sonrasi = string(bayt)

	oncekiSatirlar := strings.Split(strings.TrimRight(oncesi, "\n"), "\n")
	sonrakiSatirlar := strings.Split(strings.TrimRight(sonrasi, "\n"), "\n")
	if len(oncekiSatirlar) == len(sonrakiSatirlar) {
		for i := range oncekiSatirlar {
			if oncekiSatirlar[i] != sonrakiSatirlar[i] {
				degisenSatir++
			}
		}
	}
	return oncesi, sonrasi, degisenSatir
}

// CalistirVeCikisAl kaynagi gecici bir modulde calistirip standart ciktisini dondurur.
func CalistirVeCikisAl(kaynak string) string {
	dizin, _ := os.MkdirTemp("", "sinama")
	defer os.RemoveAll(dizin)
	yeniModul(dizin)
	os.WriteFile(filepath.Join(dizin, "main.go"), []byte(kaynak), 0o644)

	cmd := exec.Command("go", "run", ".")
	cmd.Dir = dizin
	cmd.Env = ortam()
	cikti, _ := cmd.Output()
	return strings.TrimRight(string(cikti), "\n")
}

// InceleyiciYakaliyorMu kaynagi gecici bir modulde derleyip inceleyiciden gecirir;
// derleyicinin gectigi ama inceleyicinin reddettigi kaliplari yakalamak icin kullanilir.
func InceleyiciYakaliyorMu(kaynak string) (derlendiMi, inceleyiciYakaladiMi bool) {
	dizin, _ := os.MkdirTemp("", "sinama")
	defer os.RemoveAll(dizin)
	yeniModul(dizin)
	os.WriteFile(filepath.Join(dizin, "main.go"), []byte(kaynak), 0o644)

	derle := exec.Command("go", "build", "-o", os.DevNull, "./...")
	derle.Dir = dizin
	derle.Env = ortam()
	derlendiMi = derle.Run() == nil

	incele := exec.Command("go", "vet", "./...")
	incele.Dir = dizin
	incele.Env = ortam()
	inceleyiciYakaladiMi = incele.Run() != nil
	return derlendiMi, inceleyiciYakaladiMi
}
```

`Bicimlendir`, kaynağı bir dosyaya yazıp biçimlendiriciyi çağırıyor, sonra aynı dosyayı
tekrar okuyor — biçimlendirme kaynağı **yerinde** değiştirdiği için öncesi ile sonrası iki
ayrı okuma. Satır sayıları eşitse (biçimlendirici satır eklemediyse ya da silmediyse)
karşılık gelen satırları teker teker karşılaştırıp kaçının değiştiğini sayıyor.
`InceleyiciYakaliyorMu` ise iki ayrı komutu art arda çağırıyor: önce derleyici, sonra
inceleyici — ikisinin de yalnız başarı/başarısızlık durumu okunuyor, hiçbir çıktı metni
saklanmıyor ya da yazdırılmıyor.

#### Bir Kaynak, Bir Kalıp

Aşağıdaki ölçüm iki ayrı soruyu tek bir koşumda topluyor: aynı düzensiz kaynak
biçimlendiriciden geçirildiğinde ne kadarı değişiyor ve ayrı bir kaynak inceleyiciden
geçirildiğinde derleyicinin gözden kaçırdığı kalıp yakalanıyor mu? İki ölçüm birbirinden
bağımsız — biri bir kaynağın görünüşünü, öbürü bir başka kaynağın geçerliliğini sınıyor —
ama ikisi de aynı yordamla, komutu program içinden çağırıp yalnız sonucu okuyarak
yürütülüyor.

```go
// main.go — bicimlendirici kaynagi degistiriyor, inceleyici derleyicinin reddetmedigi bir kalibi yakaliyor
package main

import (
	"fmt"
	"strings"

	"ders/araclar"
)

func main() {
	kaynak := "package main\n\n" +
		"import \"fmt\"\n\n" +
		"type Nokta struct {\n" +
		"  X int\n" +
		"  Y int\n" +
		"  Etiket string\n" +
		"}\n\n" +
		"func Topla(a int,b int) int {\n" +
		"      return a+b\n" +
		"}\n\n" +
		"func main() {\n" +
		"    n := Nokta{X: 1, Y: 2, Etiket: \"a\"}\n" +
		"    fmt.Println(n,Topla(2,3))\n" +
		"}\n"

	oncesi, sonrasi, degisenSatir := araclar.Bicimlendir(kaynak)
	toplamSatir := strings.Count(strings.TrimRight(oncesi, "\n"), "\n") + 1

	fmt.Println("-- bicimlendirici kaynagi degistiriyor mu --")
	fmt.Printf("bayt bayt ayni mi: %v\n", oncesi == sonrasi)
	fmt.Printf("toplam satir: %d, degisen satir: %d\n", toplamSatir, degisenSatir)

	fmt.Println()
	fmt.Println("-- sinirlayici olcum: bicim degisiyor, anlam degismiyor --")
	ciktiOncesi := araclar.CalistirVeCikisAl(oncesi)
	ciktiSonrasi := araclar.CalistirVeCikisAl(sonrasi)
	fmt.Printf("bicimlendirme oncesi kaynagin ciktisi: %s\n", ciktiOncesi)
	fmt.Printf("bicimlendirme sonrasi kaynagin ciktisi: %s\n", ciktiSonrasi)
	fmt.Printf("iki cikti ayni mi: %v\n", ciktiOncesi == ciktiSonrasi)

	fmt.Println()
	fmt.Println("-- inceleyici, derleyicinin reddetmedigi bir kalibi yakaliyor --")
	kalipliKaynak := "package main\n\n" +
		"import \"fmt\"\n\n" +
		"func main() {\n" +
		"\tfmt.Printf(\"%d\\n\", \"metin\")\n" +
		"}\n"
	derlendiMi, yakalandiMi := araclar.InceleyiciYakaliyorMu(kalipliKaynak)
	fmt.Printf("derlendi mi: %v\n", derlendiMi)
	fmt.Printf("inceleyici yakaladi mi: %v\n", yakalandiMi)
}
```

```
-- bicimlendirici kaynagi degistiriyor mu --
bayt bayt ayni mi: false
toplam satir: 18, degisen satir: 7

-- sinirlayici olcum: bicim degisiyor, anlam degismiyor --
bicimlendirme oncesi kaynagin ciktisi: {1 2 a} 5
bicimlendirme sonrasi kaynagin ciktisi: {1 2 a} 5
iki cikti ayni mi: true

-- inceleyici, derleyicinin reddetmedigi bir kalibi yakaliyor --
derlendi mi: true
inceleyici yakaladi mi: true
```

**PK14.** 18 satırlık kaynağın 7 satırı biçimlendiriciden geçtikten sonra değişiyor; kalan 11
satır zaten kanonik biçimdeydi ve hiç dokunulmuyor. **PK15.** Değişen yedi satırın altısında
girinti karakteri boşluktan sekmeye dönüşüyor; bunlardan ikisinde yapı alanlarının tipleri aynı
sütunda hizalansın diye ayrıca boşluk ekleniyor, ikisinde bir virgülden sonraki boşluk
tamamlanıyor — biri işlev tanımının parametre listesinde, biri bir çağrıda —, birinde de bir
toplama işlecinin iki yanına boşluk giriyor — bu kategoriler bazı satırlarda üst üste biniyor (bir
girinti düzeltmesiyle bir hizalama aynı satırda birlikte geçebiliyor), ama hiçbiri yeni bir satır
eklemiyor ya da bir satırı siliyor.

Yazılmayan şey burada bir **tercih**: kaynağı yazan kişi girinti için boşluk mu sekme mi
kullanacağını, bir yapının alanlarını hizalayıp hizalamayacağını, virgülden sonra boşluk
bırakıp bırakmayacağını yazmadı. Biçimlendirici bu boşlukları kendi kanonik kararıyla
dolduruyor — ve bu, kursun sıfır değer bahsindeki "tanımlı" sınıfının bir başka görünümü:
yazılmayan bir şey, derleyicinin reddiyle değil, ayrı bir aracın kendi kuralıyla
dolduruluyor.

Bu kararın tek bir kanonik biçimde durması, birden çok kişinin aynı kaynak üzerinde
çalıştığı durumlarda ayrı bir sonuç doğuruyor. Girinti ve hizalama tercihe bağlı
kaldığında, aynı anlama gelen iki kaynak yalnız görünüş farkıyla birbirinden ayrışabiliyor
— ve bu fark, kaynağın önceki hâliyle karşılaştırıldığında (satır satır ne değiştiğine
bakıldığında) gürültü olarak görünüyor: hangi satırın **anlamca** değiştiği, hangisinin
yalnız **biçimce** değiştiği birbirine karışıyor. Biçimlendiricinin kanonik kararı bu
gürültüyü kaynağında kesiyor — yazan kişi hangi girintiyi seçerse seçsin, biçimlendiriciden
geçtikten sonra kaynak her zaman aynı biçime dönüşüyor ve bu yüzden iki kaynak arasındaki
gerçek fark yalnız biçimlendirilmiş iki hâlin karşılaştırılmasıyla görülebiliyor.

#### İnceleyici, Derleyicinin Geçtiği Bir Kalıbı Yakalıyor

**PK16.** `fmt.Printf("%d\n", "metin")` çağrısı derleniyor — `Printf`'in değişken sayıda
argümanı `interface{}` tipinde kabul ettiği için, bir dizginin bir `%d` damgasının karşılığı
olarak geçirilmesi derleyicinin gözünde sözdizimsel olarak geçerli. **PK17.** Aynı kaynak
inceleyiciden geçirildiğinde reddediliyor: `%d` bir tam sayı beklerken argümanın bir dizgi
olması, biçim damgası ile değerin tipi arasındaki bir uyumsuzluk ve bu uyumsuzluk
derleyicinin denetlediği bir kural değil.

Bu ayrım kursun "reddedildi" sınıfını genişletiyor. Önceki konularda reddeden taraf hep
derleyiciydi; burada reddeden taraf **inceleyici** — ayrı bir komut, ayrı bir geçiş, ayrı
bir kural kümesi. Derleyici yalnız kaynağın sanal makinenin çalıştırabileceği bir programa
dönüşüp dönüşemeyeceğine bakıyor; `Printf`'in biçim dizgisi ile argümanları arasındaki
uyum bu sorunun dışında kalıyor, çünkü biçim dizgisi derleyicinin gözünde sıradan bir
dizgi değeri, özel bir anlamı yok. İnceleyici bu dizgiyi **ayrıştırıp** her damgayı
karşılık gelen argümanla eşleştiriyor ve eşleşmeyeni yakalıyor — derleyicinin hiç bakmadığı
bir katmanda çalışıyor.

Bu yakalamanın değeri, hatanın nerede ortaya çıkacağını değiştirmesinde. `%d` yerine bir
dizgi geçirilmiş bir çağrı derleniyor ve çalışıyor — ama çalıştığında beklenen sayı yerine
biçim damgasının kendisini ve bir hata işareti basıyor ve bu yalnız o çağrı satırı
gerçekten çalıştırıldığında görünüyor. Bir kaynak tabanında böyle bir çağrı seyrek
çalışan bir dala düşmüşse, kusur uzun süre fark edilmeyebilir; derleme hiçbir uyarı
vermediği için de kaynağı okuyan biri bunu kaynağın kendisinden çıkaramaz. İnceleyici bu
çağrıyı **hiç çalıştırmadan**, yalnız kaynağı okuyarak yakalıyor — bu, kursun "açıkta"
sınıfına düşecek bir kusuru, çalışma zamanına hiç bırakmadan "reddedildi" sınıfına
taşıyan bir araç. Derleyicinin kendisi bu taşımayı yapmıyor, çünkü derleyicinin görevi
sözdizimini ve tip uyumunu denetlemekle sınırlı; biçim dizgisinin içeriği derleyicinin
tip sisteminin dışında kalan, yalnız çalışma zamanında yorumlanan bir dizgi değeri.

#### Sınırlayıcı Ölçüm: Biçim Değişiyor, Anlam Değişmiyor

**PK18.** Aynı kaynağın biçimlendirme öncesi ve sonrası hâli, ikisi de ayrı ayrı
çalıştırıldığında birebir aynı çıktıyı veriyor: `{1 2 a} 5`. Bayt bayt karşılaştırma
`false` derken, çalıştırma çıktısı karşılaştırması `true` diyor — bu iki sonuç birbirini
çürütmüyor, tam tersine dersin tezini kuruyor: biçimlendirici kaynağın **görünüşünü**
değiştiriyor, **davranışını** değiştirmiyor.

Bunun nedeni biçimlendiricinin ne yaptığında yatıyor: girinti, boşluk ve hizalama —
biçimlendiricinin dokunduğu her şey — dilin sözdiziminde **anlamsız** boşluk. Bir `int`
değişkeninin adı, bir işlevin gövdesindeki ifadelerin sırası, bir yapının alan adları —
bunların hiçbiri biçimlendirme sırasında değişmiyor, çünkü biçimlendirici bunlara hiç
dokunmuyor. Sınır burada net: biçimlendirici bir **ayrıştırma ağacını** okuyup aynı ağacı
kanonik boşluklarla yeniden yazıyor, ağacın kendisini hiç değiştirmiyor. Bu yüzden
biçimlendirilmiş bir kaynağın davranışı hakkında hiçbir yeni varsayım gerekmiyor — ölçülen
programın çalıştırıldığında ne yaptığı, biçimlendirmeden önce ne yapıyorsa biçimlendirmeden
sonra da onu yapıyor.

Bu sınır, inceleyicinin sınırından farklı bir yerde duruyor. Biçimlendirici kaynağı hiç
reddetmiyor — kendisine verilen her geçerli kaynağı kabul edip yeniden yazıyor ve
girdinin "iyi" ya da "kötü" olduğuna hiç karar vermiyor. İnceleyici ise tam tersini
yapıyor: kaynağı hiç değiştirmiyor, yalnız reddediyor ya da geçiriyor. İki araç da aynı
komut ailesinin üyesi, ikisi de derleyicinin bakmadığı bir katmanda çalışıyor, ama biri
kaynağı dönüştürerek, öbürü kaynağı yargılayarak iş görüyor. Bu ders ikisini de aynı
yordamla — kaynağı geçici bir modüle yazıp komutu çağırarak — sınadı ve ikisinin de
sonucu yalnız bir bayrak (değişti mi, geçti mi) olarak okundu; hiçbir aşamada bir hata
metni ya da bir biçimlendirilmiş diff satır satır yazdırılmadı.

#### Özet

- Dilin kendi komut ailesi yalnız derlemeyi değil biçimlendirmeyi ve incelemeyi de kapsar;
  üçü de bu derste olduğu gibi program içinden, argüman olarak çağrılabilir.
- Biçimlendirici, kendisine verilen kaynağı tek bir kanonik biçime dönüştürür; 18 satırlık
  bir kaynağın 7 satırı bu dönüşümde değişir, geri kalanı zaten kanonik biçimdeydi.
- İnceleyici, derleyicinin reddetmediği bir kalıbı (biçim damgası ile argüman tipinin
  uyuşmaması) yakalar; derleyici bu uyumu hiç denetlemez, çünkü biçim dizgisi onun
  gözünde sıradan bir dizgi değeridir.
- Sınırlayıcı ölçüm: biçimlendirme öncesi ve sonrası kaynak, çalıştırıldığında birebir
  aynı çıktıyı verir — biçim değişir, anlam değişmez.
- İki aracın adı da yalnız kod bloğunda, bir çağrının argümanı olarak geçti; düzyazıda
  yalnız iş adlarıyla anıldı.

#### Sonraki Adım

Bu derste bir kaynağın biçimi ve derleyicinin gözden kaçırdığı bir kalıbı kimin
uyguladığı ölçüldü — ikisi de dilin kendi komut ailesinin ayrı üyeleri, ikisi de derleyicinin
bakmadığı bir katmanda çalışıyor. Ama bu üç aracın (derleyici, biçimlendirici, inceleyici)
hiçbiri, bir paketin dışarıya ne sunduğunu anlatan metni — bir adın ne işe yaradığını
açıklayan yazıyı — hiç okumuyor. Kursun son dersi bu son köşeye bakıyor: bir belge
kaynaktan nasıl üretiliyor, hangi adlar bu belgeye hiç girmiyor ve belge aracı bir
yorumun **doğruluğunu** denetliyor mu — yoksa yalnız **konumunu** mu?

### Belgeleme Sözleşmeleri

Source: https://academia.sh/tr/kurslar/go-temelleri/belgeleme-sozlesmeleri

Altı ad — üç dışa aktarılan, üç dışa aktarılmayan; her üçlüde biri bitişik yorumlu, biri araya boş satır giren yorumlu, biri yorumsuz — belge aracına soruluyor. Görünen adların sayısı yalnız harfin büyüklüğüne bağlı (3/6), açıklaması olanların sayısı ise yalnız yorumun konumuna bağlı (1/6). Sınırlayıcı ölçüm, doğru olmayan bir belge yorumunun da aynen görüntülendiğini gösteriyor: belge aracı yorumun doğruluğunu hiç denetlemiyor.

Önceki üç ders, dilin kendi komut ailesinin üç üyesini çağırdı: derleyici bir kaynağın
geçerli olup olmadığına karar verdi, biçimlendirici görünüşünü kanonikleştirdi, inceleyici
derleyicinin gözden kaçırdığı bir kalıbı yakaladı. Üçü de kaynağın **kendisiyle**
ilgileniyordu. Bu son ders ailenin dördüncü üyesine bakıyor: bir paketin dışarıya ne
sunduğunu anlatan metni üreten belge aracına. Bu araç kaynağı ne derliyor ne
biçimlendiriyor ne de bir kalıba karşı sınıyor — yalnız **okuyor** ve hangi adı, hangi
yorumla, ne zaman göstereceğine karar veriyor. Kursun son sorusu: bu kararı veren şey
yorumun kendisi mi, yoksa adın baş harfi mi?

#### Bitişik Yorum, Ayrık Yorum

**PK19.** Bir yorum, bir tanıma **bitişik** yazıldığında (araya boş satır girmeden) o
tanımın belge yorumu sayılır; araya bir boş satır girdiğinde aynı yorum metni artık hiçbir
tanıma bağlanmaz. **PK20.** Belge aracı yalnız dışa aktarılan adları listeler; dışa
aktarılmayan bir adın belge yorumu ne kadar özenli yazılırsa yazılsın, o ad belgeye hiç
girmez.

```go
// araclar/araclar.go — belge aracini program icinden cagiran ortak yordamlar
package araclar

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

func ortam() []string {
	return append(os.Environ(), "GOTOOLCHAIN=local")
}

// GecidiKur verilen paket kaynagini "sinama/pk" alt paketine yazip modulun kok
// dizinini dondurur; cagiran bu dizinde belge aracini calistirir.
func GecidiKur(pkKaynagi string) string {
	dizin, _ := os.MkdirTemp("", "sinama")
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.MkdirAll(filepath.Join(dizin, "pk"), 0o755)
	os.WriteFile(filepath.Join(dizin, "pk", "pk.go"), []byte(pkKaynagi), 0o644)
	return dizin
}

// BelgedeGorunuyorMu bir adi belge aracina sorar; ad belgede varsa gorundu true doner,
// aciklamaVar ise satirin altinda bir yorum metni olup olmadigini soyler.
func BelgedeGorunuyorMu(dizin, ad string) (gorundu, aciklamaVar bool) {
	cmd := exec.Command("go", "doc", "./pk."+ad)
	cmd.Dir = dizin
	cmd.Env = ortam()
	cikti, err := cmd.Output()
	if err != nil {
		return false, false
	}
	satirlar := strings.Split(strings.TrimRight(string(cikti), "\n"), "\n")
	bosOlmayan := 0
	for _, s := range satirlar {
		if strings.TrimSpace(s) != "" {
			bosOlmayan++
		}
	}
	// Cikti yalniz paket basligi ve imza satiriyken iki bos olmayan satir tasir;
	// bir aciklama eklendiginde bu sayi artar.
	return true, bosOlmayan > 2
}

// BelgeMetni bir adin belge aracindan donen tam ciktisini dondurur.
func BelgeMetni(dizin, ad string) string {
	cmd := exec.Command("go", "doc", "./pk."+ad)
	cmd.Dir = dizin
	cmd.Env = ortam()
	cikti, _ := cmd.Output()
	return strings.TrimSpace(string(cikti))
}
```

`BelgedeGorunuyorMu`, belge aracını doğrudan bir adla çağırıyor: ad bulunursa çıktı en az
paket başlığı ve imza satırını taşıyor, bulunamazsa araç sıfır olmayan bir çıkış koduyla
dönüyor ve hata metni hiç okunmuyor. Açıklama olup olmadığı, çıktının kaç satır taşıdığına
bakılarak sayılıyor — imza satırının altına bir açıklama eklendiğinde çıktı üçüncü bir
boş olmayan satır kazanıyor.

#### Altı Ad, İki Bağımsız Soru

```go
// main.go — belge kaynaktan uretiliyor, gorunurluk harfe, aciklama konuma bagli
package main

import (
	"fmt"
	"os"

	"ders/araclar"
)

func main() {
	pkKaynagi := "// Paket pk belge yorumu deneyleri icin kucuk bir kutuphanedir.\n" +
		"package pk\n\n" +
		"// BuyukYorumlu, verilen iki sayinin toplamini dondurur.\n" +
		"func BuyukYorumlu(a, b int) int { return a + b }\n\n" +
		"// kucukYorumlu, verilen iki sayinin toplamini dondurur.\n" +
		"func kucukYorumlu(a, b int) int { return a + b }\n\n" +
		"func BuyukYorumsuz(a, b int) int { return a - b }\n\n" +
		"func kucukYorumsuz(a, b int) int { return a - b }\n\n" +
		"// BuyukAyrikYorum bir aciklamadir ama tanimdan bos satirla ayriliyor.\n\n" +
		"func BuyukAyrikYorum(a, b int) int { return a * b }\n\n" +
		"// kucukAyrikYorum bir aciklamadir ama tanimdan bos satirla ayriliyor.\n\n" +
		"func kucukAyrikYorum(a, b int) int { return a * b }\n\n" +
		"// Ortalama, verilen sayilarin ortalamasini dondurur.\n" +
		"func Ortalama(sayilar ...int) int {\n" +
		"\ttoplam := 0\n" +
		"\tfor _, s := range sayilar {\n" +
		"\t\ttoplam += s\n" +
		"\t}\n" +
		"\treturn toplam\n" +
		"}\n"

	dizin := araclar.GecidiKur(pkKaynagi)
	defer os.RemoveAll(dizin)

	adlar := []string{
		"BuyukYorumlu", "kucukYorumlu",
		"BuyukYorumsuz", "kucukYorumsuz",
		"BuyukAyrikYorum", "kucukAyrikYorum",
	}

	fmt.Println("-- alti ad, belgede goruntu ve aciklama --")
	gorunenSayisi, aciklamaliSayisi := 0, 0
	for _, ad := range adlar {
		gorundu, aciklamaVar := araclar.BelgedeGorunuyorMu(dizin, ad)
		if gorundu {
			gorunenSayisi++
		}
		if aciklamaVar {
			aciklamaliSayisi++
		}
		fmt.Printf("%-18s gorundu=%-5v aciklama=%v\n", ad, gorundu, aciklamaVar)
	}
	fmt.Printf("toplam: %d/6 gorundu, %d/6 aciklamali\n", gorunenSayisi, aciklamaliSayisi)

	fmt.Println()
	fmt.Println("-- sinirlayici olcum: belge araci yorumun dogrulugunu denetlemiyor --")
	fmt.Println(araclar.BelgeMetni(dizin, "Ortalama"))
	fmt.Printf("Ortalama(2, 4, 6) gercek sonucu: %d\n", gercekOrtalama(2, 4, 6))
}

// gercekOrtalama, uretilen Ortalama islevinin gercekte ne yaptigini bagimsiz olarak
// gosterir: yorum "ortalama" diyor, islev toplami donduruyor.
func gercekOrtalama(sayilar ...int) int {
	toplam := 0
	for _, s := range sayilar {
		toplam += s
	}
	return toplam
}
```

```
-- alti ad, belgede goruntu ve aciklama --
BuyukYorumlu       gorundu=true  aciklama=true
kucukYorumlu       gorundu=false aciklama=false
BuyukYorumsuz      gorundu=true  aciklama=false
kucukYorumsuz      gorundu=false aciklama=false
BuyukAyrikYorum    gorundu=true  aciklama=false
kucukAyrikYorum    gorundu=false aciklama=false
toplam: 3/6 gorundu, 1/6 aciklamali

-- sinirlayici olcum: belge araci yorumun dogrulugunu denetlemiyor --
package pk // import "sinama/pk"

func Ortalama(sayilar ...int) int
    Ortalama, verilen sayilarin ortalamasini dondurur.
Ortalama(2, 4, 6) gercek sonucu: 12
```

**PK21.** Altı addan üçü belgede görünüyor, üçü hiç görünmüyor; görünenlerin üçü de büyük
harfle başlıyor, görünmeyenlerin üçü de küçük harfle. **PK22.** Görünen üç addan yalnız
biri açıklama taşıyor — yorumu tanıma bitişik olan `BuyukYorumlu`; yorumu olmayan
`BuyukYorumsuz` ile yorumu araya boş satırla ayrılan `BuyukAyrikYorum`, ikisi de belgede
adıyla görünüyor ama açıklamasız.

Bu iki gözlem birbirinden tamamen bağımsız iki soruya yanıt veriyor. "Bu ad belgede var mı?"
sorusunun yanıtı yalnız harfin büyüklüğüne bakıyor — `kucukAyrikYorum`'un yorumu
`BuyukAyrikYorum`'unkiyle birebir aynı biçimde ayrık, ama ikisinin belgedeki kaderi hiç aynı
değil, çünkü aradaki fark yorumda değil adın kendisinde. "Bu adın açıklaması var mı?" sorusunun
yanıtı ise yalnız yorumun konumuna bakıyor — `BuyukYorumsuz`'un hiç yorumu yok;
`BuyukAyrikYorum`'un ise bir yorumu var ama tanımdan kopuk; ikisi de belgede açıklamasız
görünüyor, çünkü ikisinde de belge aracının okuyacağı bitişik bir yorum yok.

#### Sınırlayıcı Ölçüm: Belge Aracı Doğruluğu Denetlemiyor

**PK23.** `Ortalama` işlevinin belge yorumu "verilen sayıların ortalamasını döndürür"
diyor; işlevin kendisi toplamı döndürüyor. `Ortalama(2, 4, 6)` çağrıldığında dönen değer
`12` — ortalama değil, toplam. **PK24.** Belge aracı bu yorumu, doğruluğunu hiç sınamadan,
aynen basıyor; ne derleyici ne inceleyici ne de belge aracı bu uyuşmazlığı yakalıyor.

Bu kursun son `açıkta` satırı ve öncekilerden farklı bir yerde duruyor. Önceki `açıkta`
örnekleri (ilk değeri verilmeyen bir sözlüğe yazmanın panikle durması, bir dizginin
baytla indekslenmesi) hep **çalışma zamanında** ortaya çıkan bir sonuçtu — programı
çalıştırdığında bir şey oluyordu. Burada açıkta kalan şey çalışma zamanıyla hiç ilgili
değil: `Ortalama` işlevi hatasız çalışıyor, panik atmıyor, yanlış bir tip döndürmüyor —
yalnız **adının söylediğiyle yaptığı şey uyuşmuyor** ve bu uyuşmazlığı denetleyecek
hiçbir araç yok. Derleyici bir dizginin içeriğini denetlemediği gibi, bir yorumun içeriğini
de denetlemiyor; inceleyici biçim damgalarıyla argüman tiplerini eşleştiriyor ama bir
yorumun anlattığıyla kodun yaptığını karşılaştırmıyor; belge aracı bu iki dosyayı
(kaynak ve yorum) birbirine bağlamıyor bile, yalnız yorumu konumuna göre kopyalayıp
gösteriyor. Yazılmayan şey burada bir **denetim mekanizması** ve dil bunun yerine hiçbir
şey koymuyor.

#### Kurs Kapanışı

Go Temelleri tek bir soruyla açıldı: bir program yazarken en çok işe yarayan şey, yazılanın
ne yaptığı değil, yazılmayanın ne olduğudur. On sekiz ders bu soruyu üç düzenekte sordu:
dilin kendisinde (sıfır değerler, dilim ve sözlük davranışı), derleyicide (kullanılmayan
değişkenden dışa aktarma kuralına kadar reddedilen kalıplar) ve kaynağın dışında (sürüm
seçimi, biçim, belge).

Birinci iddia doğrulandı: Go'da "başlatılmamış" diye bir durum yok — yazılmayan bir
değerin yerini hep tanımlı bir karşılık dolduruyor, ister bir sıfır değer olsun ister bir
dilim sınırının varsayılanı. İkinci iddia, derleyicinin reddettiği kalıpların hepsinin
"yazılmayanın karşılığını daraltma" kararı olduğunu gösterdi — kullanılmayan bir
değişkenden dışa aktarılmayan bir alana kadar, derleyici bazı yazılmamışlıkları hiç kabul
etmiyor. Üçüncü iddia, tanımlılığın **okuma yönünde çalışıp yazma ve yorumlama yönünde
bittiğini** kurdu: bu son derste bunun en uç örneği görüldü — bir yorumun okunması
tanımlı, ama o yorumun **doğru olup olmadığı** hiçbir yerde denetlenmiyor.

Kurs aşırı bir genelleme yapmadan kapanıyor: bu üç sınıf (tanımlı, reddedildi, açıkta)
Go'nun bütün davranışını kapsamıyor, yalnız bu on sekiz dersin sorduğu soruya — bunu
yazmadığında ne oluyor? — verilen yanıtları sınıflıyor.

| Ders | Ölçülen yazılmamışlık | Karşılığı (tanımlı · reddedildi · açıkta) | Sınırlayıcı ölçüm |
|---|---|---|---|
| Go'nun Tasarım Amaçları | Dört ret nedeni: kullanılmayan değişken/içe aktarım, yazılmayan dönüş, örtük dönüşüm | reddedildi | Örtük dönüşümün reddi kaynağı kısaltmıyor: derlenmeyen satır 15, derlenen karşılığı 22 karakter |
| Ortam Kurulumu ve İlk Program | Çalışmak için zorunlu üç parça: paket bildirimi, `func main`, modül dosyası | reddedildi | Modül dosyası kaynağın dışında durur; kaynak hiç değişmeden, yalnız modül dosyası kaldırılınca derleme durur |
| Değişkenler ve Sabitler | Altı sabitten yalnız ikisinin değeri kaynakta yazılı, dördü `iota` ile üretiliyor | tanımlı (`iota`) · reddedildi (paket düzeyinde kısa bildirim) | Kısa bildirim yalnız fonksiyon içinde geçerli; aynı satır paket düzeyinde reddediliyor |
| Temel Tipler | Tipsiz sabitin varsayılan tipi `int`; `int8` sınırını aşan toplam -128'e sarıyor | tanımlı (varsayılan tip, taşma) · açıkta (kayan noktalı karşılaştırma) | Örtük dönüşümün reddi sabitlerde geçerli değil; tipsiz sabit hedef tipe kendiliğinden uyuyor |
| Sıfır Değerler | On beş yazılmamışlığın dağılımı — kursun bağlayıcı sayısı burada üretiliyor | 8 tanımlı · 4 reddedildi · 3 açıkta | `nil` dilime eklenebiliyor, `nil` sözlüğe yazılamıyor; ikisi de aynı biçimde sıfır değer |
| Denetim Akışı | `for`un üç bölümünün isteğe bağlılığı, ifadesiz `switch`, yazılmayan `break` | tanımlı (beş biçimin beşi) | Varsayılanın tersi bedelsiz değil: düşmeyi istemek `fallthrough` yazmayı gerektiriyor |
| Fonksiyonlar | Adlandırılmış dönüşün gövdede hiç yazılmaması; dönen değerin hiç yakalanmaması | tanımlı (sıfır dönüş) · reddedildi (dönüşün yazılmaması) · açıkta (yok sayılan dönüş) | Dönen değer bir ada bağlanınca kullanılmayan değişken reddi devreye giriyor |
| Değişken Sayıda Argüman | Hiç argüman verilmeyen parametrenin `nil` dilim olması | tanımlı | `nil` dilim ile boş dilim `len` açısından aynı, kimliğe bakan işlemlerde ayrı |
| Diziler ve Dilimler | Atama satırında dizi/dilim farkı: dizide kopya, dilimde paylaşım | tanımlı | `append` kapasiteyi aştığında paylaşım kesiliyor; kopyalanan başlık artık ayrı bir alt diziyi gösteriyor |
| Dilim İşlemleri | Yazılmayan dilimleme sınırlarının varsayılanı | tanımlı | Aynı `append` çağrısı, aynı kaynak metinle, iki ayrı kapasitede iki ayrı sonuç veriyor |
| Sözlükler | Dört erişim biçiminin sınıfı: var olan/olmayan anahtar, `nil` sözlükten okuma/yazma | tanımlı · açıkta (yazma panikliyor) | İki yanıtlı erişim olmadan, değeri sıfır olan bir kayıt ile hiç var olmayan kayıt ayırt edilemiyor |
| Yapılar | Yazılmayan alanların sıfır değeri; gömmenin yazılmayan adları dış tipe taşıması | tanımlı | Gömme kalıtım değil: gömülü tipin yöntemi çağrıldığında alıcı gömülü değerin kendisi, dış yapı değil |
| İşaretçiler | Adres alınmadan geçirilen dizi ve yapının çağıranda değişmemesi | tanımlı (kopya) · açıkta (`nil` işaretçiden alan okuma) | İşaretçi de bir değerdir ve kopyalanır; yeniden atamak yalnız yerel kopyayı değiştiriyor |
| Dizgiler ve Rune'lar | `len` bayt sayıyor, indeksleme bayt veriyor, `range` `rune` veriyor | tanımlı (bayt sözü) · açıkta (karakterin karşılığı yok) | Rune sayısı da karakter sayısı değil; birleşik kod noktaları bunu bozuyor |
| Paket Düzeni ve Görünürlük | Dışa aktarmanın bir harfe bağlı olması | reddedildi (paket dışından) · tanımlı (paket içinde) | Sınır paket sınırıdır, dosya sınırı değil; aynı paketin iki dosyası küçük harfli bir adı hiç `import` olmadan paylaşıyor |
| Modüller ve Bağımlılıklar | Yazılmayan bağımlılık sürümü | tanımlı (en düşük sürüm seçimi) | Seçilen sürüm kataloğun en yenisi değil, yazılan (ya da türetilen) isteklerin en büyüğü |
| Go Komut Ailesi | Yazılmayan kaynak biçimi; derleyicinin reddetmediği bir kalıp | tanımlı (biçim) · reddedildi (inceleyici) | Biçim değişiyor (18 satırın 7'si), anlam değişmiyor: biçimlendirme öncesi ve sonrası kaynağın çıktısı aynı |
| Belgeleme Sözleşmeleri | Belgeye giren ad ve açıklama | tanımlı (harfe göre görünürlük) · açıkta (içerik denetlenmiyor) | Belge aracı yorumun doğruluğunu denetlemiyor; yanlış bir açıklama da aynen basılıyor |

#### Özet

- Belge aracı yalnız dışa aktarılan adları listeler; bir adın belgede görünüp
  görünmeyeceğini yalnız ilk harfinin büyüklüğü belirler, yorumun kendisi değil.
- Bir yorumun açıklama sayılması, tanıma **bitişik** yazılmasına bağlıdır; araya giren tek
  bir boş satır, yorumu o tanımdan koparır ve belgede görünmez kılar.
- Altı adın üçü belgede görünür (yalnız büyük harfliler), görünenlerin yalnız biri
  açıklama taşır (yalnız bitişik yorumlu olan) — iki soru birbirinden bağımsızdır.
- Sınırlayıcı ölçüm: belge aracı bir yorumun doğru olup olmadığını hiç denetlemez; yanlış
  bir açıklama, doğru bir açıklamayla aynı biçimde basılır.
- Kurs üç sınıfla (tanımlı, reddedildi, açıkta) kapandı; bu sınıflar Go'nun bütün
  davranışını değil, yalnız bu on sekiz dersin sorduğu tek soruyu kapsıyor.

