---
title: 'Paket Düzeni ve Görünürlük'
source: 'https://academia.sh/tr/kurslar/go-temelleri/paket-duzeni-ve-gorunurluk'
course: 'Go Temelleri'
language: tr
updated: '2026-08-23T14:25:01+00:00'
license: 'CC BY-SA 4.0'
---

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

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, 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 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, 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.
- Dört çapraz paket denemesinden ikisi (büyük harfli işlev, yalnız dışa aktarılan alanı
  kuran yapı literali) derlenir; ikisi (küçük harfli işlev, 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?
