Yeni bir dil öğrenmeye başlarken en çok merak edilen şey genelde sözdizimidir: Döngü nasıl yazılır, fonksiyon nasıl tanımlanır? Oysa bir dili gerçekten anlamak için önce neden var olduğunu bilmek gerekir. Go, 2007'de Google'da üç mühendisin — Robert Griesemer, Rob Pike ve Ken Thompson — büyük bir C++ projesinin derlenmesini beklerken duydukları sıkıntıdan doğdu. Milyonlarca satırlık kod tabanları yavaş derleniyor, bağımlılıklar içinden çıkılmaz hale geliyor, çok çekirdekli makinelerden verim almak zorlaşıyordu.
Go'nun cevabı radikal biçimde basit oldu: Dili küçük tut, derlemeyi saniyeler içinde bitir, eşzamanlılığı dilin kendisine koy. Bugün Docker, Kubernetes, Terraform, Prometheus gibi altyapı dünyasının bel kemiği olan araçlar Go ile yazılmış durumda. Go'nun 25 anahtar kelimesi vardır; C++'ın 90'dan fazla. Bu azlık bir eksiklik değil, bilinçli bir tercihtir: Kodu okuyan kişi, yazandan daha önemlidir.
Bu derste Go'yu kuracak, ilk modülünü oluşturacak, ilk programını yazıp çalıştıracaksın. Ardından her Go geliştiricisinin günde onlarca kez kullandığı araçları — go run, go build, gofmt, go vet — ve ekrana yazdırmanın tüm yollarını öğreneceksin. Dersin sonunda kendi makinende çalışan, biçimi standartlara uygun, denetimden geçmiş bir Go programın olacak.
Go'nun tasarım felsefesi
Go'yu diğer dillerden ayıran birkaç kararlı tercih vardır. Bunları baştan bilmek, ilerideki derslerde "neden böyle?" sorusunu azaltır:
- Az özellik, çok netlik. Go'da sınıf, kalıtım, istisna (exception), operatör aşırı yüklemesi ve — uzun süre — jenerikler yoktu. Jenerikler 1.18'de eklendi ama diğerleri hâlâ yok. Bir problemi çözmenin genelde tek bir bariz yolu vardır.
- Tek bir biçim.
gofmtaracı kodu tek bir standart biçime sokar. Go dünyasında girinti tartışması yapılmaz; tartışmayı araç bitirmiştir. - Hatalar değerdir. Bir işlem başarısız olabiliyorsa fonksiyon ikinci bir dönüş değeri olarak
errordöndürür.try/catchyoktur; hata yolu kodun görünür bir parçasıdır. - Eşzamanlılık dilin içindedir.
goanahtar kelimesiyle bir fonksiyonu ayrı bir yürütme akışında başlatırsın. Bunun için kütüphane gerekmez. - Derleme hızlıdır. Kullanılmayan bir import veya değişken bile derleme hatasıdır. Sert görünür ama kod tabanını temiz ve derlemeyi hızlı tutar.
- Statik derleme. Go, tek bir çalıştırılabilir dosya üretir. Sunucuya kopyala ve çalıştır; ayrı bir çalışma zamanı kurmana gerek yoktur.
Kurulum ve doğrulama
go.dev/dl adresinden işletim sistemine uygun kurulum dosyasını indir. macOS'ta .pkg, Windows'ta .msi, Linux'ta ise arşivi /usr/local altına açman yeterlidir. Kurulumdan sonra terminalde:
go versionBeklenen çıktı buna benzer bir satırdır (sürüm ve platform sende farklı olabilir):
go version go1.26.0 darwin/arm64Bu satırı görüyorsan Go kurulmuş ve PATH'e eklenmiş demektir. Komut bulunamıyorsa Go'nun bin dizinini PATH'e eklemen gerekir. Kurulumun ayrıntılarını görmek için:
go env GOROOT GOPATH GOMODCACHEGOROOT Go'nun kurulu olduğu yeri, GOPATH indirilen paketlerin ve araçların yerini gösterir. Modüller çıktıktan sonra GOPATH'in önemi büyük ölçüde azaldı; projelerini artık istediğin dizinde tutabilirsin.
Modül oluşturma: go mod init
Go'da her proje bir modüldür (module). Modül, birlikte sürümlenen paketlerin kökünü ve bağımlılıklarını tanımlar. Yeni bir proje başlatmak iki komut sürer:
mkdir merhaba
cd merhaba
go mod init example.com/merhabaBu, dizinde go.mod adlı bir dosya oluşturur:
module example.com/merhaba
go 1.26İlk satır modülün yolunu, ikinci satır hedeflenen Go sürümünü belirtir. Modül yolu genelde kodun yayımlanacağı adrestir (github.com/kullanici/proje gibi). Yalnızca kendi makinende deneme yapıyorsan example.com/merhaba gibi bir ad yeterlidir; önemli olan başka modüllerle çakışmamasıdır.
Projene dışarıdan bir paket eklediğinde go.mod dosyasına bir require satırı, dizine de bağımlılıkların kriptografik özetlerini tutan go.sum dosyası eklenir. Paketler ve modüller konusunu Paketler ve Modüller dersinde ayrıntılı işleyeceğiz.
İlk program: package main ve func main
Dizinde main.go adında bir dosya oluştur:
package main
import "fmt"
func main() {
fmt.Println("Merhaba, dünya!")
}Merhaba, dünya!
Bu altı satırın her parçası önemlidir:
package main— Her Go dosyası bir pakete aittir ve ilk satırda bunu bildirir.mainözel bir pakettir: Derleyici yalnızcamainpaketinden çalıştırılabilir bir program üretir. Adı başka bir şey olsaydı (package hesapgibi) bu kod bir kütüphane olurdu.import "fmt"— Standart kütüphanedekifmt(format) paketini içeri alır. Biçimli giriş/çıkış işlemleri bu paketten gelir. Go'da içeri aldığın ama kullanmadığın bir paket derleme hatasıdır.func main()— Programın giriş noktasıdır. Parametre almaz, değer döndürmez.mainfonksiyonu bittiğinde program da biter.fmt.Println(...)— Paket adı, nokta ve fonksiyon adı. Büyük harfle başlayan isimler (Println) paket dışından erişilebilir, küçük harfle başlayanlar erişilemez. Go'dapublic/privateanahtar kelimeleri yoktur; görünürlüğü ismin ilk harfi belirler.
Go'da satır sonlarına noktalı virgül koymazsın. Aslında dil bunları ister, ama derleyici satır sonlarına kendisi ekler. Bunun bir yan etkisi vardır: Açılış süslü parantezi ({) mutlaka aynı satırda kalmalıdır. Aşağıdaki gibi bir yazım derlenmez:
func main()
{ // HATA: syntax error: unexpected semicolon or newline before {
fmt.Println("...")
}Araç zinciri: run, build, fmt, vet
Go'nun tek bir go komutu vardır; alt komutlarıyla derler, çalıştırır, biçimlendirir, test eder ve bağımlılıkları yönetir. Günlük hayatta en çok kullanacakların şunlardır:
./... yazımı "bu dizin ve altındaki tüm paketler" anlamına gelir; büyük projelerde her yeri tek komutla taramak için kullanılır.
gofmt: biçim tartışması yok
gofmt girintiyi sekme ile yapar, operatörlerin etrafındaki boşlukları düzenler, yapıları hizalar. Editörün Go eklentisi genelde kaydettiğin anda gofmt (ya da eşdeğeri go fmt) çalıştırır. Biçim bozuk bir dosyayı düzeltmek için:
gofmt -w main.goDeğişikliği uygulamadan farkı görmek istersen -d bayrağını kullan: gofmt -d main.go.
go vet: derleyicinin bakmadığı yere bakar
Derleyici sözdizimini ve tipleri kontrol eder; ama bazı hatalar tip açısından geçerlidir. go vet bu tür şüpheli desenleri yakalar. Klasik örnek, Printf fiilleriyle argümanların uyuşmamasıdır:
fmt.Printf("%d yaş\n", "otuz") // vet: arg "otuz" for %d of wrong type: string
fmt.Println("%d yaş\n", 30) // vet: Println call has possible Printf formatting directiveİkisi de derlenir, ikisi de yanlıştır. go vet ./... komutunu düzenli çalıştırmayı alışkanlık haline getir.
Ekrana yazdırma: Println ve Printf
fmt paketinin üç temel yazdırma ailesi vardır. Farkı anlamak ilk günden işini kolaylaştırır:
Print— Argümanları olduğu gibi yazar, satır sonu eklemez.Println— Argümanların arasına boşluk koyar ve sonuna satır sonu ekler.Printf— Bir biçim dizesi (format string) alır;%ile başlayan fiiller (verbs) argümanların nasıl yazılacağını belirler.
package main
import "fmt"
func main() {
name := "Zeynep"
age := 28
height := 1.6543
active := true
fmt.Print("satır sonu yok")
fmt.Print(" | araya boşluk koymaz\n")
fmt.Println(name, age, active) // araya boşluk koyar, satır sonu ekler
fmt.Printf("%s %d yaşında\n", name, age)
fmt.Printf("boy: %.2f m\n", height) // 2 basamak
fmt.Printf("%v | %v | %v\n", name, age, active) // varsayılan biçim
fmt.Printf("%q ve %T\n", name, height) // tırnaklı metin ve tip
fmt.Printf("%5d|%-5d|%05d|\n", 42, 42, 42) // genişlik ve hizalama
fmt.Printf("ikilik: %b, onaltılık: %x\n", 200, 200)
fmt.Printf("yüzde işareti: %%\n")
}satır sonu yok | araya boşluk koymaz Zeynep 28 true Zeynep 28 yaşında boy: 1.65 m Zeynep | 28 | true "Zeynep" ve float64 42|42 |00042| ikilik: 11001000, onaltılık: c8 yüzde işareti: %
En sık kullanacağın fiiller:
Yazdırmak yerine metni bir değişkende tutmak istersen Sprintf ailesini kullanırsın; Fprintf ise çıktıyı bir dosyaya veya hata akışına yazar:
package main
import (
"fmt"
"os"
)
func main() {
total := 3
msg := fmt.Sprintf("%d dosya işlendi", total) // yazdırmaz, döndürür
fmt.Println(msg)
fmt.Println(len(msg), "bayt")
fmt.Fprintln(os.Stdout, "standart çıktıya")
fmt.Fprintln(os.Stderr, "standart hataya") // günlük ve hata mesajları buraya
}3 dosya işlendi 16 bayt standart çıktıya standart hataya
Yorum satırları
Go iki yorum biçimini destekler:
// Tek satırlık yorum: satır sonuna kadar geçerlidir.
/*
Blok yorum: birden çok satır.
İç içe yazılamaz.
*/Pratikte Go kodunda neredeyse her zaman // kullanılır. Blok yorumlar genelde dosya başındaki telif/lisans notlarına ayrılır.
Bir tanımın (paket, fonksiyon, tip, değişken) hemen üstüne yazılan yorumlar belge yorumu (doc comment) sayılır; go doc ve pkg.go.dev bunları okur. Kural, yorumun tanımlanan adla başlamasıdır:
// Package hesap basit finansal hesaplamalar sağlar.
package hesap
// KDVEkle, verilen tutara oran kadar KDV ekleyip sonucu döndürür.
// oran 0.20 gibi ondalık bir değer olarak verilir.
func KDVEkle(tutar, oran float64) float64 {
return tutar * (1 + oran)
}Yorumların ne yaptığını değil, neden öyle yapıldığını anlatması iyi bir alışkanlıktır. i++ satırının yanına "i'yi bir artır" yazmak kimseye bir şey katmaz.
Sık yapılan hatalar
- Kullanılmayan import veya değişken bırakmak.
imported and not used: "os"ya dadeclared and not used: xhatası alırsın. Deneme yaparken geçici olarak_ = xyazabilirsin ama kalıcı çözüm satırı silmektir. - Açılış parantezini alt satıra almak. Go, satır sonlarına otomatik noktalı virgül ekler;
{mutlaka aynı satırda kalmalıdır. Printlniçinde biçim fiili kullanmak.fmt.Println("%d adet", n)metni olduğu gibi yazar. Biçimlendirme istiyorsanPrintfkullan ve satır sonunu (\n) kendin ekle.go run main.goile tek dosya çalıştırmak. Projede birden çok dosya varsa eksik tanım hatası alırsın.go run .tüm paketi derler.- Dosyayı
go mod inityapmadan bir dizine koymak. Modül dışındago runçalışmaz; hata mesajı doğrudango mod initönerir. - Dışa açılacak ismi küçük harfle başlatmak.
func toplam()başka paketlerden görünmez;func Toplam()görünür.
Alıştırmalar
Adını, yaşını ve bir hobini değişkenlerde tutan; ardından şu üç satırı yazdıran bir program yaz:
Ad: Elif
Yaş: 24
Hobi: dalışİlk satırda Println, ikinci ve üçüncü satırlarda Printf kullan.
İpucu
Printf'in sonuna \n koymayı unutma; Println'den farklı olarak satır sonunu kendisi eklemez.
Çözümü göster
package main
import "fmt"
func main() {
name := "Elif"
age := 24
hobby := "dalış"
fmt.Println("Ad:", name)
fmt.Printf("Yaş: %d\n", age)
fmt.Printf("Hobi: %s\n", hobby)
}Ad: Elif Yaş: 24 Hobi: dalış
Println argümanların arasına otomatik boşluk koyduğu için "Ad:" ile isim arasında boşluk oluşur. Printf'te boşluğu biçim dizesinin kendisi sağlar.
Bir ürünün adını, adedini ve birim fiyatını alıp aşağıdaki gibi hizalı bir fiş satırı yazdır. Ürün adı sola yaslı 12 karakter, adet sağa yaslı 3 karakter, tutar iki ondalık basamaklı olsun. Toplam tutarı Sprintf ile bir değişkende oluştur ve sonra yazdır.
Ürün Adet Tutar
kalem 3 37.50İpucu
Sola yaslamak için %-12s, sağa yaslamak için %3d, iki basamak için %8.2f kullan.
Çözümü göster
package main
import "fmt"
func main() {
name := "kalem"
count := 3
unitPrice := 12.5
total := float64(count) * unitPrice
fmt.Printf("%-12s %4s %9s\n", "Ürün", "Adet", "Tutar")
line := fmt.Sprintf("%-12s %4d %9.2f", name, count, total)
fmt.Println(line)
}Ürün Adet Tutar kalem 3 37.50
count bir int, unitPrice bir float64 olduğu için çarpmadan önce float64(count) dönüşümü gerekir: Go farklı tipleri örtük olarak dönüştürmez. Sprintf metni döndürür, böylece satırı yazdırmadan önce saklayabilir veya değiştirebilirsin.
Sekizli hizalamada Türkçe karakterlerin birden fazla bayt tuttuğunu unutma; %-12s genişliği rune değil bayt sayar. Bu ayrıntıyı Stringler ve Rune'lar dersinde göreceksin.
Bir tam sayı, bir ondalık sayı, bir metin ve bir boolean değer için her satırda değerin kendisini (%v) ve tipini (%T) hizalı biçimde yazdıran bir program yaz. Metin için ayrıca tırnaklı gösterimi (%q), tam sayı için de 2'lik, 8'lik ve 16'lık tabandaki karşılıklarını göster.
İpucu
Sütunları hizalamak için %-10v ve %-10T gibi genişlikli fiiller kullan. Taban dönüşümleri için %b, %o ve %x yeterli; sıfır dolgusu istersen %08b yaz.
Çözümü göster
package main
import "fmt"
func main() {
n := 42
f := 3.14159
s := "Go"
b := true
fmt.Printf("%-10v %-10T\n", n, n)
fmt.Printf("%-10v %-10T\n", f, f)
fmt.Printf("%-10v %-10T %q\n", s, s, s)
fmt.Printf("%-10v %-10T\n", b, b)
fmt.Printf("\n%d sayısı: 2'lik %b, 8'lik %o, 16'lık %x\n", n, n, n, n)
fmt.Printf("sıfır dolgusu: %08b\n", n)
}42 int 3.14159 float64 Go string "Go" true bool 42 sayısı: 2'lik 101010, 8'lik 52, 16'lık 2a sıfır dolgusu: 00101010
%v her tip için makul bir varsayılan gösterim üretir; %T tipin adını yazar ve tip dönüşümü hatalarını ayıklarken çok işine yarar. %q yalnızca metin (ve rune) için anlamlıdır: Bir int ile kullanırsan program çökmez, çıktıya %!q(int=42) gibi bir işaret koyar ve go vet bunu "wrong type" diye uyarır.
Kısa sınav
Bir Go dosyasının ilk satırındaki package main bildirimi ne anlama gelir?
fmt.Println("%d adet", 3) ne yazdırır?
gofmt aracının Go ekosistemindeki rolü nedir?
Aşağıdakilerden hangisi Go'da derleme hatasına yol açar?
go run . ile go build arasındaki temel fark nedir?
Bir fonksiyonun başka paketlerden erişilebilir olmasını ne belirler?
Özet
- Go, büyük kod tabanlarında derleme hızı, okunabilirlik ve eşzamanlılık sorunlarına cevap olarak tasarlandı; az sayıda özellikle çok iş yapmayı hedefler.
- Her proje bir modüldür;
go mod init <yol>ilego.moddosyası oluşturulur. - Çalıştırılabilir program için dosya
package mainile başlar vefunc main()içerir; büyük harfle başlayan isimler dışa açıktır. go run .geliştirirken,go builddağıtım için kullanılır;gofmtbiçimi,go vetşüpheli desenleri,go testtestleri yönetir.Printlnbasit yazdırma,Printfbiçimli yazdırma,Sprintfmetin döndürme içindir; en sık kullanılan fiiller%v,%d,%s,%q,%Tve%.2f'tir.- Kullanılmayan import ve yerel değişkenler derleme hatasıdır; bu katılık kod tabanını temiz tutar.
- Belge yorumları tanımın hemen üstüne yazılır ve tanımlanan adla başlar;
go docve pkg.go.dev bu yorumları gösterir.