---
title: Şablonlar
source: 'https://academia.sh/tr/kurslar/go-uygulamalar/sablonlar'
course: 'Standart Kütüphane ve Ağ Uygulamaları'
language: tr
updated: '2026-08-23T16:55:13+00:00'
license: 'CC BY-SA 4.0'
---

# Şablonlar

Aynı şablon metni iki farklı Go tipinden (bir tamsayı, bir ondalıklı sayı) geçirildiğinde bayt bayt aynı çıktıyı üretir; kaynaktaki tip bilgisi çıkışta tümüyle kayboluyor. Bir alan adı çıktıda yalnızca şablonun kendi metninde yazılmışsa görünür — tip sisteminden gelmez. Metin ve HTML şablonları aynı veriyi kaçırma bakımından farklı bayt dizilerine çevirir. Sınırlayıcı ölçüm: şablon alan adlarını çalışma zamanında arar, olmayan bir alan derlemede değil çalıştırmada belli olur, ve hata çıktının ortasında doğar.

Önceki dersin son ölçümü bir yapı etiketinin ne işe yaradığını gösterdi: yapı etiketi
alan adını sınırın karşı tarafına taşıyan bir tarifti, ve o tarif olmadan geri kurma da
yoktu. Bu ders aynı soruyu tersinden soruyor. JSON dizileştirme bir yapıyı bayta çevirip
geri getirdi — gidiş ile dönüş arasında bir tarif vardı ve tarif, dört alandan beşinin
tipini yeniden kurdu. Şablonlar bu geri dönüşü hiç vaat etmiyor: bir şablonun ürettiği
şey yalnızca metin, ve o metnin bir daha hiçbir tarifle okunmayacağı varsayılıyor. Kursun
sorusu aynı kalıyor — bu değer sınırı geçerken tip bilgisine ne oluyor? — ama bu kez sınırın
karşı tarafı bir program değil, bir ekran ya da bir dosya.

Go'nun standart kitaplığı iki şablon paketi taşır: `text/template` ve `html/template`.
İkisi de aynı şablon dilini kullanır — `{{.Alan}}` bir alanı okur, `{{if}}` ve `{{range}}`
dallanma ve yineleme sağlar — ve ikisi de bir Go değerini alıp bir `io.Writer`'a metin
yazar. Aralarındaki tek fark, birazdan ölçülecek olan kaçırma davranışı. Her ikisi de
paket adıyla, kod bloğunda çağrılıyor; düzyazıda yalnızca "metin şablonu" ve "HTML şablonu"
olarak anılıyorlar.

`Execute` yönteminin ikinci parametresi bir `io.Writer`dır — bu ders arabellek (`bytes.Buffer`)
kullanıyor, ama aynı çağrı bir dosyaya, standart çıktıya ya da bir ağ bağlantısına da
yazabilirdi; Sistem ve Veri Erişimi konusunun ilk dersi bu üç hedefin aynı arayüzü
paylaştığını ayrıca ölçecek.
Şablon paketinin kendisi hedefin ne olduğunu bilmiyor ve bilmesi de gerekmiyor — yalnızca
bir dizi bayt üretip yazıyor. Bu ders tam olarak bu üretilen baytların içinde neyin durup
neyin durmadığını ölçüyor: kaynak veri sınırı geçerken, tip sistemi Go'nun kendi tarafında
kalıyor mu, yoksa şablonun ürettiği metne kadar mı taşınıyor?

Kursun ortak ölçümü on üç sınır geçişini üç sınıfa ayırmıştı: birinde tip korunuyor, beşinde
yeniden kuruluyor, yedisinde kayboluyor (03. derste kurulan tam dağılım). Şablon çıktısı bu
yedi kayıp geçişten biri — ama kayboluşun **biçimi** JSON'daki kayıptan farklı, çünkü JSON
bir geri dönüş biçimidir (bayta çevrilir, sonra geri okunabilir) ve şablon hiç geri dönmeyi
vaat etmez; ürettiği metnin bir daha ayrıştırılacağı bile varsayılmaz.

## Şablonun Tek Çıktı Tipi: Metin

**AG19.** Bir şablonun `Execute` çağrısının ürettiği şey, kaynak verinin tipi ne olursa
olsun, her zaman bir bayt dizisidir — ve o bayt dizisi bir dizgiye dönüştürüldüğünde
kaynaktaki hiçbir tip bilgisini taşımaz. Bunu göstermenin en doğrudan yolu, aynı şablon
metnini iki farklı Go tipinden geçirip çıktıları karşılaştırmaktır.

```go
// sablonlar.go — sablonun urettigi metnin tipini, alan izini, kacirma farkini ve calisma zamani aramasini olcer
package main

import (
	"bytes"
	"fmt"
	"html/template"
	"strings"
	textTemplate "text/template"
)

type Urun struct {
	Ad    string
	Adet  int
	Yorum string
}

type UrunTamsayi struct{ Adet int }
type UrunOndalikli struct{ Adet float64 }

func main() {
	urun := Urun{Ad: "vida", Adet: 12, Yorum: "iyi <b>ama</b> pahali & yavas"}

	sabitSablon := textTemplate.Must(textTemplate.New("t").Parse("{{.Adet}}"))
	var bTamsayidan, bOndaliklidan bytes.Buffer
	sabitSablon.Execute(&bTamsayidan, UrunTamsayi{Adet: 12})
	sabitSablon.Execute(&bOndaliklidan, UrunOndalikli{Adet: 12})
	fmt.Println("-- ayni sablon, iki farkli kaynak tipi --")
	fmt.Println("tamsayidan gelen cikti:", bTamsayidan.String())
	fmt.Println("ondalikli sayidan gelen cikti:", bOndaliklidan.String())
	fmt.Println("iki cikti bayt bayt ayni mi:", bTamsayidan.String() == bOndaliklidan.String())

	dar := textTemplate.Must(textTemplate.New("dar").Parse("{{.Ad}} x{{.Adet}}"))
	var bDar bytes.Buffer
	dar.Execute(&bDar, urun)
	fmt.Println()
	fmt.Println("-- alan adi ciktida yalniz sablon metninde yazilmissa gorunur --")
	fmt.Println("dar sablon ciktisi:", bDar.String())
	fmt.Println("ciktida \"Ad\" adi geciyor mu:", strings.Contains(bDar.String(), "Ad"))
	fmt.Println("ciktida \"Adet\" adi geciyor mu:", strings.Contains(bDar.String(), "Adet"))

	genis := textTemplate.Must(textTemplate.New("genis").Parse("Ad: {{.Ad}}, Adet: {{.Adet}}"))
	var bGenis bytes.Buffer
	genis.Execute(&bGenis, urun)
	fmt.Println("genis sablon ciktisi:", bGenis.String())
	fmt.Println("ciktida \"Ad\" adi geciyor mu:", strings.Contains(bGenis.String(), "Ad"))

	metinSablon := textTemplate.Must(textTemplate.New("m").Parse("{{.Yorum}}"))
	var bMetin bytes.Buffer
	metinSablon.Execute(&bMetin, urun)
	htmlSablon := template.Must(template.New("h").Parse("{{.Yorum}}"))
	var bHtml bytes.Buffer
	htmlSablon.Execute(&bHtml, urun)
	fmt.Println()
	fmt.Println("-- ayni veri, metin ve html sablonundan gecince farkli bayt uretiyor --")
	fmt.Printf("metin sablonu: %s\n", bMetin.String())
	fmt.Printf("html sablonu:  %s\n", bHtml.String())
	fmt.Println("iki cikti ayni mi:", bMetin.String() == bHtml.String())

	hatali := textTemplate.Must(textTemplate.New("hatali").Parse("on: {{.Ad}}, sonra: {{.OlmayanAlan}}"))
	var bHatali bytes.Buffer
	hata := hatali.Execute(&bHatali, urun)
	fmt.Println()
	fmt.Println("-- olmayan alan calisma zamaninda hata veriyor, once yazilan kismi yazidirilmis oluyor --")
	fmt.Println("hata olustu mu:", hata != nil)
	fmt.Printf("hatadan once yaziya gecen kisim: %q\n", bHatali.String())
}
```

```
-- ayni sablon, iki farkli kaynak tipi --
tamsayidan gelen cikti: 12
ondalikli sayidan gelen cikti: 12
iki cikti bayt bayt ayni mi: true

-- alan adi ciktida yalniz sablon metninde yazilmissa gorunur --
dar sablon ciktisi: vida x12
ciktida "Ad" adi geciyor mu: false
ciktida "Adet" adi geciyor mu: false
genis sablon ciktisi: Ad: vida, Adet: 12
ciktida "Ad" adi geciyor mu: true

-- ayni veri, metin ve html sablonundan gecince farkli bayt uretiyor --
metin sablonu: iyi <b>ama</b> pahali & yavas
html sablonu:  iyi &lt;b&gt;ama&lt;/b&gt; pahali &amp; yavas
iki cikti ayni mi: false

-- olmayan alan calisma zamaninda hata veriyor, once yazilan kismi yazidirilmis oluyor --
hata olustu mu: true
hatadan once yaziya gecen kisim: "on: vida, sonra: "
```

**AG20.** `UrunTamsayi.Adet` bir `int`, `UrunOndalikli.Adet` bir `float64` — ikisi de
aynı `{{.Adet}}` şablonundan geçtiğinde çıktı ikisinde de `12` dizgisidir, bayt bayt eşit.
Şablon çalışırken kaynağın hangi tipten geldiğini biliyordu (`Execute` bir arayüz değeri
alır ve alanına yansımayla erişir), ama ürettiği metnin içinde bu bilgiden hiçbir iz
kalmıyor. Çıktıyı okuyan bir program `12` dizgisinden geriye doğru "bu bir tamsayı mıydı,
ondalıklı mıydı" sorusunu yanıtlayamaz.

Bu, önceki dersteki tarifsiz sözlük gözlemiyle aynı yüzeye iniyor ama farklı bir yoldan.
Bir JSON değeri tarifsiz bir kaba çözüldüğünde sayı hep `float64` oluyordu — kayıp vardı,
ama kayıp **belirli bir yöndeydi**: her sayı aynı tek tipe düşüyordu, ve bu düşüş
`encoding/json`'ın kendi kuralıydı. Şablon çıktısında böyle bir kural bile yok, çünkü
ortada hedef bir tip hiç yok — `int` de `float64` de aynı `string`e düşüyor, ama bu
düşüşün "tek bir hedef tipe toplanma" gibi bir yapısı yok; ikisi de sıradan metin
oldu ve metnin kendisi hiçbir tipe ait değil. JSON'un kaybı bir **dönüştürücünün**
kararıydı; şablonun kaybı, ortada dönüştürülecek bir hedef tipin **hiç bulunmamasının**
sonucu.

**AG21.** Dar şablon (`{{.Ad}} x{{.Adet}}`) ürettiği `vida x12` metninde ne `"Ad"` ne
`"Adet"` sözcüğü geçiyor — alan adları da tip bilgisi kadar kayboluyor, çünkü şablonun
yazdığı şey alanın **değeri**, alanın **adı** değil. Geniş şablon (`Ad: {{.Ad}}, Adet:
{{.Adet}}`) çıktısında `"Ad"` görünüyor, ama bunun nedeni Go'nun yapı alanının adını
otomatik olarak taşıması değil — şablonu yazan kişi `"Ad: "` dizgisini kendi eliyle,
düz metin olarak şablonun içine yazdı. Alan adının çıktıda görünmesi, tip sisteminden
gelen bir garanti değil, şablon metnini yazan kişinin bir kararı.

Şablonun çıktısının saf metin olması bir sınırlama olduğu kadar bir esneklik de taşıyor:
hedefin bir HTML belgesi olması zorunlu değil. `text/template` bir yapılandırma dosyası,
bir e-posta gövdesi, bir günlük satırı, hatta başka bir dilde yazılmış bir kaynak dosyası
üretmek için de kullanılabilir — üretilen şey ne olursa olsun, şablon paketinin gözünde
fark etmiyor, çünkü paket yalnızca bir metni bir başka metne, alan değerlerini yerine
koyarak çeviriyor. Kursun bir sonraki dersi bu son kullanımı, kaynak kod üretimini,
tam olarak bu noktadan devralıyor.

`UrunTamsayi` ve `UrunOndalikli` bilinçli olarak tek alanlı, birbirine paralel iki tip
olarak tanımlandı: aralarındaki tek fark `Adet` alanının tipi, başka hiçbir şey değişmiyor.
Bu, ölçümün gösterdiği şeyin yalnızca "tip bilgisi kayboluyor" olduğunu, başka bir yapısal
farktan (alan sayısı, alan sırası, alan adı) kaynaklanmadığını netleştiriyor — iki tip aynı
şablon eylemine (`{{.Adet}}`) aynı adla yanıt veriyor ve aradaki tek değişken tip olduğu
için, çıktıdaki eşitlik doğrudan bu tek değişkene bağlanabiliyor.

## Metin ile HTML Şablonunun Kaçırma Farkı

**AG22.** Aynı `Urun` değeri, aynı `{{.Yorum}}` şablon metniyle, iki farklı pakete
verildiğinde iki farklı bayt dizisi üretiyor. `text/template`, `Yorum` alanındaki
`<b>` ve `&` karakterlerine hiç dokunmadan olduğu gibi yazıyor. `html/template` aynı
karakterleri `&lt;`, `&gt;` ve `&amp;` dizgilerine çeviriyor — bu dönüşüm **kaçırma**
(M14/K01 Web'in Temelleri ve HTML kursunda kurulan terim). `html/template` bunu
otomatik yapıyor çünkü çıktısının bir HTML belgesinin içine yerleşeceğini varsayıyor
ve `<`, `>`, `&` karakterlerinin HTML'de özel bir anlamı olduğunu biliyor; `text/template`
hiçbir varsayım yapmıyor, çünkü çıktısının nereye gideceğini bilmiyor.

M14/K01 ve M14/K02 kursları HTML üretimini ve kaçırma davranışını tarayıcı tarafında,
sunucudan gelen bir belgenin işlenmesi bağlamında kurdu; burada tekrarlanmıyor, tek
fark bu iki paketin aynı standart kitaplığın içinde, sunucu tarafında bir üretim adımı
olarak durması. İki paket arasındaki seçim bir stil tercihi değil: çıktı bir HTML sayfasına
gidecekse `html/template` kullanılmalı, çünkü kaçırmayı atlayan bir metin şablonu, veri
içinde `<script>` gibi bir dizi geçtiğinde bunu olduğu gibi yazan bir çıktı üretir.

`html/template`in kaçırması bu ölçümde göründüğünden daha ayrıntılı çalışıyor: paket bir
alanın şablonun **neresinde** kullanıldığını (bir etiket gövdesinde mi, bir öznitelik
değerinde mi, bir bağlantı adresinde mi) ayrıştırma sırasında saptıyor ve her bağlama
uygun kaçırma kuralını uyguluyor — bir öznitelik değerindeki tırnak işareti ile bir etiket
gövdesindeki `<` karakteri farklı kurallarla kaçırılıyor. Bu ders yalnızca en basit bağlamı
(bir etiket gövdesi) ölçüyor; bağlam farkındalığının kendisi ayrı bir konu ve burada
derinleştirilmiyor. Önemli olan sonuç şu: `text/template` çıktısını hangi bağlamda
kullanacağını hiç bilmiyor ve hiçbir karakteri değiştirmiyor, `html/template` ise çıktısının
bir HTML belgesine yerleşeceğini varsayıyor ve buna göre davranıyor. Kaçırılmamış bir
kullanıcı girdisi bir HTML belgesinin içine olduğu gibi yerleştiğinde, tarayıcı onu düz
metin değil **yapı** olarak okuyabilir — kaçırmanın kendisi, aradan geçen değerin görünümünü
değil, karşı tarafın onu nasıl yorumlayacağını değiştiren bir adım.

## Alan Adı Çalışma Zamanında Aranır

**AG23.** `{{.OlmayanAlan}}` ifadesi `Urun` tipinde var olmayan bir alanı istiyor.
Bu şablon derleniyor — çünkü şablon metni Go'nun derleyicisinin hiç görmediği, ayrı bir
dilde yazılmış bir dizgi; derleyici `Urun` tipinin hangi alanlara sahip olduğunu bilir
ama bir dizginin içindeki `{{.OlmayanAlan}}` ifadesini hiç okumaz. Alan araması,
`Execute` çağrıldığında, yansıma üzerinden **çalışma zamanında** yapılıyor, ve o an
alan bulunamayınca `Execute` bir hata döndürüyor. Kursun "yazılmayanın karşılığı"
sorusuna (M09/K01'de kurulan eksen, burada tekrar kullanılmıyor) benzer bir soru burada
da geçerli: derleyicinin hiç bakmadığı bir katman, kendi hatasını kendi zamanında üretiyor.

Şablonun kendi hata zamanları da tek değil. `template.Must` çağrısı, `Parse`in döndürdüğü
hatayı sarıp panikliyor — bu ders boyunca hiç tetiklenmedi, çünkü her şablonun metni
sözdizimsel olarak geçerliydi (`{{` her zaman bir `}}` ile kapandı, her eylem tanınan bir
eylemdi). `Parse` hatası, örneğin kapanmamış bir `{{` parçası için, programın en başında,
şablon kurulurken çıkar. `Execute` hatası ise sözdizimi tamamen geçerliyken, yalnızca
istenen alan yokken çıkıyor. İkisi de çalışma zamanında (Go'nun derleyicisi ikisini de
görmüyor), ama biri şablon kurulur kurulmaz, öbürü ancak gerçek veriyle çalıştırıldığında
ortaya çıkıyor — ve bu ders yalnız ikinciyi ölçtü, çünkü ilkini tetikleyecek hiçbir örnek
kullanılmadı.

**AG24.** `hatali` şablonu `"on: {{.Ad}}, sonra: {{.OlmayanAlan}}"` metnini taşıyor ve
iki ayrı eylem içeriyor: önce `{{.Ad}}` başarıyla değerlendiriliyor ve `"on: vida, sonra:
"` metni `bHatali` arabelleğine yazılıyor; sonra `{{.OlmayanAlan}}` başarısız oluyor ve
`Execute` bir hata dönüyor. Ama arabellek geri alınmıyor — hatadan önce yazılmış olan
kısım orada kalıyor, çünkü `Execute` metni **ilerleyerek** yazıyor, bir sonuç biriktirip
en sonda tek seferde yazmıyor. Bu, ölçümün son satırında görünen `"on: vida, sonra: "`
dizgisinin nereden geldiğini açıklıyor: hata, çıktının ortasında, ikinci alan aranırken
doğuyor, ve o ana kadar üretilmiş her şey `io.Writer`'a zaten ulaşmış oluyor.

**AG25.** Bu, kursun "reddedildi" ile "açıkta kaldı" ayrımına yeni bir görünüm ekliyor.
Derleyici alan adı hatasını hiç görmüyor — kaynak her koşulda derleniyor — ve hata ne
derleme zamanında ne de şablon ayrıştırılırken (`Parse` çağrısında) çıkıyor; yalnızca
`Execute` çalıştığında, ve o çalıştırma anında yazılmış olan çıktı kalıcı oluyor. Bir
şablonu üretime almadan önce sınamanın tek yolu, onu gerçek verilerle **çalıştırmaktır**;
alan adlarının doğruluğunu statik olarak denetleyen bir araç bu ikilinin dışında kalıyor.

Bunun pratik sonucu, bir şablonun içindeki nadiren tetiklenen bir dalın (bir `{{if}}`
koşulunun yalnızca belirli bir veriyle geçtiği bir satırın) aylarca hiç çalıştırılmadan
durabilmesi. Go Temelleri kursunun Go Komut Ailesi dersi, inceleyicinin derleyicinin
gözden kaçırdığı bir kalıbı yakaladığını göstermişti — biçim dizgisi ile argüman tipinin
uyuşmaması. Şablon alan adları için böyle bir ikinci denetleyici yok; şablon dilinin
kendisi Go'nun tip sisteminin dışında, ayrı bir dilde yaşıyor ve derleyicinin de
inceleyicinin de erişemediği bir katmanda çalışıyor. Bu yüzden bir şablonun doğruluğu,
derlemenin verdiği güvenceyle değil, o şablonu gerçek veriyle en az bir kez çalıştırmış
olmanın verdiği güvenceyle ölçülüyor — ve bu ders bu ölçümü tam olarak koşturarak yaptı.

## Özet

- Bir şablonun ürettiği şey her zaman metindir; kaynağın tipi ne olursa olsun (`int`,
  `float64`) aynı şablon aynı dizgiyi üretir ve çıktıdan geriye doğru kaynağın tipine
  ulaşılamaz.
- Alan adları da çıktıda otomatik olarak yer almaz — bir ad çıktıda görünüyorsa, bunun
  nedeni şablonu yazan kişinin o adı düz metin olarak şablona yazmış olmasıdır.
- `text/template` ve `html/template` aynı şablon dilini paylaşır, ama `html/template`
  çıktısını HTML kaçırmasından geçirir; aynı veri iki pakette iki farklı bayt dizisi
  üretir.
- Şablondaki bir alan adı derleme zamanında değil, `Execute` çalışırken, yansımayla
  aranır; olmayan bir alan şablonun derlenmesini değil çalıştırılmasını durdurur.
- Hata, çıktının ortasında doğar: `Execute` hata döndürmeden önce yazdığı kısım
  `io.Writer`'da kalıcı olarak durur, geri alınmaz.

## Sonraki Adım

Bu derste bir şablonun ürettiği çıktının tip taşımayan, saf metin olduğu ölçüldü — ve
bu metnin okunması için karşı tarafta hiçbir tarif kurulmamıştı. Kursun bir önceki
dersi (JSON dizileştirme) ile bu ders arasındaki fark tam olarak burada duruyor: biri
tarifle geri dönen bir gidiş, öbürü hiç geri dönmeyen bir çıkış. Ama iki dersin de ortak
bir noktası var: ikisinde de tarif, varsa, sınırın yalnızca **bir tarafında** duruyordu —
JSON'da alıcıdaki hedef tip, şablonda hiç kimse. Sıradaki ders bu iki ucu birleştiriyor:
iki ayrı taraf, aynı tarifi paylaştığında sınırı geçen tip bilgisine ne olur? Sözleşme
temelli servis iletişimi bu soruyu, tarifin kendisini iki tarafın kaynağını üretecek
biçimde kurarak yanıtlıyor — ve kursun on üç sınır geçişi arasında tip bilgisinin
korunduğu tek satırı orada kuruyor.
