---
title: "Dizgiler ve Rune'lar"
source: 'https://academia.sh/tr/kurslar/go-temelleri/dizgiler-ve-runelar'
course: 'Go Temelleri'
language: tr
updated: '2026-08-23T14:25:00+00:00'
license: 'CC BY-SA 4.0'
---

# Dizgiler ve Rune'lar

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, UTF-8 ile kodlanmış salt okunur bir bayt dizisidir. `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 dersinde
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 dersinde 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.
