Go'da koşullu dallanma için iki yapı vardır: if ve switch. if koşulunda parantez yazılmaz ama süslü parantez zorunludur, koşul mutlaka bool olmalıdır ve koşuldan önce if v, err := f(); err != nil gibi kısa bir deyim yazılabilir. switch ise C'dekinden genel bir yapıdır: case ifadeleri sabit olmak zorunda değildir, eşleşen dal çalıştıktan sonra otomatik olarak alttaki dala geçilmez ve ifadesiz yazılan switch, uzun if-else zincirlerinin yerini alır.
Bu yazıda if sözdizimini ve kısa deyimin kapsamını, erken dönüş kalıbını, switch'in değerlendirme sırasını, birden çok değerli case'leri, fallthrough kurallarını, ifadesiz switch'i ve switch içinde break kullanımını spesifikasyon ve Effective Go'ya dayanarak anlatıyoruz. Örnekler Go 1.27.3 ile çalıştırıldı. Serinin haritası için Go öğrenme rehberi sayfasına bakabilirsiniz.
if deyimi

Spesifikasyona göre if deyimi, bir bool ifadenin değerine göre iki daldan birini çalıştırır: ifade doğruysa if dalı, değilse (varsa) else dalı. Koşul parantez içine alınmaz; gövde ise tek satır olsa bile süslü parantez içinde yazılmak zorundadır. Effective Go, zorunlu süslü parantezlerin basit if deyimlerini birden çok satıra yazmayı teşvik ettiğini, özellikle gövdede return ya da break varken bunun iyi bir stil olduğunu belirtir. else if ile zincir kurulabilir.
if'in en kullanışlı özelliği, koşuldan önce noktalı virgülle ayrılmış kısa bir deyim kabul etmesidir. Bu deyim koşuldan önce çalışır ve burada tanımlanan değişkenler yalnızca if deyiminin örtük bloğunda, yani if, else if ve else dallarının tamamında görülür; deyim bittiğinde kapsam dışına çıkar. Böylece yalnızca bir kontrol için gereken değişken dış kapsamı kirletmez.
package main
import (
"fmt"
"strconv"
)
func main() {
for _, girdi := range []string{"42", "-7", "abc"} {
if n, err := strconv.Atoi(girdi); err != nil {
fmt.Println("hata:", err)
} else if n < 0 {
fmt.Println(n, "negatif")
} else {
fmt.Println(n, "pozitif ya da sıfır")
}
// n ve err burada artık kapsam dışında
}
}Örnekte n ve err, if deyiminin başında tanımlandı ve hem else if hem else dalında kullanılabildi. Döngünün bir sonraki turunda yeniden tanımlanırlar. Kısa deyimde := kullanıldığı için aynı adlı dış değişkenleri gölgeleyeceğini unutmayın; bu konu değişkenler ve sabitler yazısında ayrıntılı anlatılıyor.
Erken dönüş: gereksiz else yazmamak
Effective Go, Go kütüphanelerinde sık görülen bir kalıba dikkat çeker: if gövdesi return, break, continue ya da goto ile bitiyorsa, gereksiz else yazılmaz. Hata durumları ortaya çıktıkça ele alınıp fonksiyondan çıkılır, başarılı akış ise sayfada yukarıdan aşağıya düz bir çizgi hâlinde ilerler. Bu, Go'daki hata yönetiminin temel görünümüdür:
f, err := os.Open(name)
if err != nil {
return err
}
d, err := f.Stat()
if err != nil {
f.Close()
return err
}
codeUsing(f, d)Bu kod Effective Go'daki örneğin aynısıdır ve bir parçadır, tek başına çalıştırılmaz. Okunabilirliğin kaynağı, her hata kontrolünün kendi içinde kapanmasıdır; iç içe else blokları birikmez. Hata değerlerinin nasıl üretileceği ve sarmalanacağı error ve hata yönetimi yazısında.
Go'da ?: operatörü yok
Go'da üçlü koşul operatörü (koşul ? a : b) bulunmaz. FAQ'ya göre dilin tasarımcıları bu operatörün anlaşılması zor karmaşık ifadeler üretmek için fazlaca kullanıldığını görmüştü; daha uzun olsa da if-else biçimi tartışmasız daha açıktır ve bir dilin tek bir koşullu akış yapısına ihtiyacı vardır. Aynı sonuç şöyle yazılır:
if expr {
n = trueVal
} else {
n = falseVal
}Sık kullanılan bir alternatif, değişkene önce varsayılan değeri verip yalnızca koşul doğruysa değiştirmektir; bu da else dalını ortadan kaldırır.
switch: ifade switch'i

Spesifikasyon iki tür switch tanımlar: ifade switch'i ve tip switch'i. İfade switch'inde switch ifadesi bir kez değerlendirilir; case ifadeleri ise soldan sağa ve yukarıdan aşağıya değerlendirilir ve switch değerine eşit olan ilk case'in deyimleri çalışır, diğerleri atlanır. Hiçbir case eşleşmezse ve bir default dalı varsa o çalışır. En fazla bir default olabilir ve switch içinde herhangi bir yerde durabilir.
C'den gelenler için en önemli fark, otomatik geçişin olmamasıdır: eşleşen dalın sonunda akış switch'in sonuna gider, her dala break yazmanız gerekmez. Birden çok değerin aynı dalı çalıştırmasını istiyorsanız değerleri virgülle ayırarak tek case'e yazarsınız. Effective Go, case ifadelerinin sabit ya da tam sayı olmak zorunda olmadığını da vurgular; string, değişken ya da fonksiyon çağrısı yazılabilir.
package main
import "fmt"
func gunTuru(gun string) string {
switch gun {
case "Cumartesi", "Pazar":
return "hafta sonu"
case "":
return "boş"
default:
return "hafta içi"
}
}
func main() {
fmt.Println(gunTuru("Pazar")) // hafta sonu
fmt.Println(gunTuru("Çarşamba")) // hafta içi
fmt.Println(gunTuru("")) // boş
x := 2
switch x {
case 1:
fmt.Println("bir")
case 2:
fmt.Println("iki")
case 3:
fmt.Println("üç")
}
// Çıktı: iki (case 3 çalışmaz, otomatik geçiş yok)
}Switch ifadesi tipsiz bir sabitse önce varsayılan tipine çevrilir; tipsiz case ifadeleri ise switch ifadesinin tipine çevrilir. Her case için x == t karşılaştırmasının geçerli olması gerekir, bu yüzden switch ifadesinin tipi karşılaştırılabilir olmalıdır. Önceden tanımlı nil değeri switch ifadesi olarak kullanılamaz.
Değerlendirme sırası ve kısa deyimli switch
Case ifadelerinin sırayla ve yalnızca eşleşme bulunana kadar değerlendirilmesi, yan etkisi olan ifadelerde fark yaratır. Aşağıdaki örnekte case listesindeki üç çağrıdan yalnızca ikisi çalıştı: ikinci çağrı 2 döndürdüğünde eşleşme bulundu ve üçüncüsü hiç değerlendirilmedi. Aynı örnek, switch'in de if gibi kısa bir başlangıç deyimi alabildiğini gösteriyor. Spesifikasyona göre switch ifadesi yazılmazsa true yazılmış sayılır; switch x := f(); { biçiminde noktalı virgülden sonraki ifadenin boş bırakılması bu anlama gelir.
package main
import (
"fmt"
"os"
)
func main() {
switch s := len(os.Args); {
case s > 1:
fmt.Println("argüman var")
default:
fmt.Println("argüman yok")
}
sayac := 0
arttir := func() int { sayac++; return sayac }
switch 2 {
case arttir(), arttir(), arttir():
fmt.Println("eşleşti, çağrı sayısı:", sayac)
}
// Çıktı (argümansız çalıştırıldığında):
// argüman yok
// eşleşti, çağrı sayısı: 2
}İfadesiz switch: if-else zincirinin yerine
Effective Go, ifadesiz switch'in bir if-else-if-else zincirini yazmanın hem mümkün hem de deyimsel bir yolu olduğunu belirtir. Her case bir bool ifadedir ve doğru olan ilk case çalışır. Aralık kontrolleri, not hesaplama ya da durum sınıflandırma gibi işlerde, uzun bir if zincirinden çok daha okunaklı bir görünüm sağlar. Case'ler yukarıdan aşağıya denendiği için sıralama önemlidir: daha dar koşulları üste yazmanız gerekir.
package main
import "fmt"
func harfNotu(puan int) string {
switch {
case puan >= 85:
return "A"
case puan >= 70:
return "B"
case puan >= 50:
return "C"
default:
return "F"
}
}
func main() {
for _, p := range []int{92, 70, 64, 12} {
fmt.Print(harfNotu(p), " ")
}
fmt.Println()
// Çıktı:
// A B C F
}fallthrough
Bir dalın bitiminde bir sonraki dalın gövdesine bilerek geçmek isterseniz fallthrough deyimini kullanırsınız. Spesifikasyona göre fallthrough, akışı bir sonraki case dalının ilk deyimine aktarır; bir sonraki dalın koşulu kontrol edilmez. Yalnızca dalın son boş olmayan deyimi olabilir, ifade switch'inin son dalında kullanılamaz ve tip switch'inde hiç kullanılamaz.
package main
import "fmt"
func main() {
seviye := 1
switch seviye {
case 1:
fmt.Println("temel kurulum")
fallthrough
case 2:
fmt.Println("güvenlik ayarları")
fallthrough
case 3:
fmt.Println("izleme")
case 4:
fmt.Println("yedekleme")
}
// Çıktı:
// temel kurulum
// güvenlik ayarları
// izleme
}Örnekte seviye 1 olduğu için ilk dal çalıştı, ardından iki fallthrough akışı sırasıyla ikinci ve üçüncü dala taşıdı. Üçüncü dalda fallthrough olmadığı için dördüncü dal çalışmadı. Pratikte fallthrough nadiren gerekir; çoğu durumda virgülle ayrılmış case listesi daha açık bir çözümdür.
switch içinde break ve etiketler
Go'da break, içinde bulunduğu en içteki for, switch ya da select deyimini sonlandırır. Bu, döngü içindeki bir switch'te sürpriz yaratır: switch dalında yazılan break döngüyü değil yalnızca switch'i bitirir. Effective Go'nun da gösterdiği çözüm, döngüye bir etiket koyup break Etiket yazmaktır:
package main
import "fmt"
func main() {
komutlar := []string{"başla", "işle", "dur", "işle"}
Dongu:
for i, k := range komutlar {
switch k {
case "dur":
fmt.Println(i, "durduruldu")
break Dongu
default:
fmt.Println(i, k)
}
}
// Çıktı:
// 0 başla
// 1 işle
// 2 durduruldu
}Etiket olmadan yazılmış bir break, "dur" komutunda yalnızca switch'ten çıkar ve döngü dördüncü elemana devam ederdi. Etiketler, continue ve döngülerle birlikte for döngüsü ve range yazısında ayrıca anlatılıyor.
Mini örnek: HTTP durum kodlarını sınıflandırma
Şimdi öğrendiklerimizi tek bir küçük programda birleştirelim. Bir HTTP istemcisinin aldığı durum kodunu hem sınıflandıran hem de isteğin yeniden denenip denenmeyeceğine karar veren iki fonksiyon yazıyoruz. durumSinifi aralık kontrolleri yaptığı için ifadesiz switch kullanıyor; her case bir bool ifadesi ve && ile iki sınırı birden kontrol ediyor. Hiçbir aralık eşleşmezse switch'ten sonraki return çalışıyor, bu yüzden bir default dalına gerek kalmadı. yenidenDene ise tek bir değeri sabit bir listeyle karşılaştırdığı için virgülle ayrılmış değerleri olan tek bir case'e sahip.
package main
import "fmt"
func durumSinifi(kod int) string {
switch {
case kod >= 200 && kod < 300:
return "başarılı"
case kod >= 300 && kod < 400:
return "yönlendirme"
case kod >= 400 && kod < 500:
return "istemci hatası"
case kod >= 500 && kod < 600:
return "sunucu hatası"
}
return "bilinmiyor"
}
func yenidenDene(kod int) bool {
switch kod {
case 429, 502, 503, 504:
return true
}
return false
}
func main() {
for _, kod := range []int{200, 301, 404, 429, 503, 99} {
if s := durumSinifi(kod); yenidenDene(kod) {
fmt.Printf("%d: %s, yeniden denenebilir\n", kod, s)
} else {
fmt.Printf("%d: %s\n", kod, s)
}
}
// Çıktı:
// 200: başarılı
// 301: yönlendirme
// 404: istemci hatası
// 429: istemci hatası, yeniden denenebilir
// 503: sunucu hatası, yeniden denenebilir
// 99: bilinmiyor
}main içindeki if deyimi, kısa deyim ile koşulun birbirinden bağımsız olabileceğini gösteriyor: kısa deyimde s değişkeni hesaplanıyor, koşul ise başka bir fonksiyonun sonucunu kontrol ediyor. s hem if hem else dalında kullanılabiliyor ve deyim bitince kapsamdan çıkıyor. Hangi kodların yeniden denenebilir sayılacağı uygulamanıza göre değişir; buradaki liste yalnızca örnek amaçlıdır. Gerçek HTTP istekleri göndermek ve yanıtları işlemek HTTP istemcisi yazısında anlatılıyor.
if mi switch mi?

| Durum | Önerilen yapı |
|---|---|
| Tek bir koşul, belki bir else | if |
| Hata kontrolü ve erken dönüş | if err != nil { return ... } |
| Tek bir değerin birçok sabitle karşılaştırılması | switch değer { case ... } |
| Birden fazla aralık ya da koşul zinciri | ifadesiz switch { case koşul: ... } |
| Bir arayüz değerinin dinamik tipine göre dallanma | type switch |
| Birden çok kanal işlemini bekleme | select |
Arayüz değerinin tipine göre dallanan switch x.(type) biçimi bu yazının kapsamı dışında; type assertion ve type switch yazısında anlatılıyor. Kanallar üzerinde bekleyen select ise select ve zaman aşımı yazısının konusu.
Sık yapılan hatalar
1. Süslü parantezi ya da else'i yeni satıra yazmak. Go'nun sözcük çözümleyicisi satır sonlarına otomatik noktalı virgül ekler. Effective Go'ya göre bunun bir sonucu, kontrol yapılarının açılış süslü parantezinin bir sonraki satıra yazılamamasıdır. Aynı kural else için de geçerlidir: kapanan parantezden sonra satır biterse oraya noktalı virgül eklenir ve alttaki else sahipsiz kalır:
package main
import "fmt"
func main() {
x := 3
if x > 2 {
fmt.Println("büyük")
}
else {
fmt.Println("küçük")
}
}# command-line-arguments
./main.go:10:2: syntax error: unexpected keyword else, expected }Doğru yazım } else { biçimindedir. gofmt kodu bu biçime getirir, ama sözdizimi hatalı bir dosyayı biçimlendiremez.
2. Aynı sabiti iki case'e yazmak. Spesifikasyon, derleyicilerin aynı sabite eşit birden çok case ifadesini reddedebileceğini belirtir; mevcut derleyiciler tam sayı, kayan nokta ve string sabitlerinde bunu yapar:
# command-line-arguments
./main.go:10:7: duplicate case 1 (constant of type int) in expression switch
./main.go:8:7: previous case3. Bool olmayan bir koşul yazmak. if n gibi bir sayı ya da işaretçi koşul olamaz; derleyici non-boolean condition in if statement hatası verir. Karşılaştırmayı açıkça yazın: if n != 0, if p != nil.
4. Otomatik geçiş beklemek. C alışkanlığıyla boş bir case bırakıp alttaki dalın çalışacağını varsaymak yanlıştır: Go'da boş case hiçbir şey yapmaz. Değerleri virgülle aynı case'e yazın.
5. switch içindeki break ile döngüden çıkılacağını sanmak. Döngüden çıkmak için etiketli break ya da fonksiyondan return kullanın.
Sık Sorulan Sorular
Go'da if koşuluna parantez yazabilir miyim?
Koşul bir ifade olduğu için if (x > 2) { biçimi derlenir, ama deyimsel değildir: Go 1.27.3 ile denediğimizde gofmt bu satırı if x > 2 { olarak yeniden yazdı. Effective Go da Go'daki kontrol yapılarının parantez kullanmadığını ve gövdenin her zaman süslü parantezle sınırlandığını belirtir.
switch case'leri neden break gerektirmiyor?
Çünkü Go'da eşleşen dalın sonunda akış otomatik olarak switch'in sonuna gider. Bir sonraki dala bilerek geçmek için fallthrough yazmanız gerekir; varsayılan davranış güvenli olanıdır.
default dalı nereye yazılmalı?
Spesifikasyona göre default switch içinde herhangi bir yerde olabilir ve yalnızca hiçbir case eşleşmediğinde çalışır. Okunabilirlik için genellikle en sona yazılır.
switch'te string ya da fonksiyon çağrısı kullanabilir miyim?
Evet. Switch ifadesinin tipi karşılaştırılabilir olduğu sürece string, değişken, fonksiyon sonucu ya da struct kullanılabilir; case ifadelerinin sabit olması gerekmez.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Go öğrenme rehberi · Go for döngüsü ve range · Go type switch · Go hata yönetimi · Go değişkenler ve sabitler · Go select