ข้ามไปยังเนื้อหา

Control Flow

if statement ใน Go คล้ายกับภาษาอื่น แต่มีความสามารถพิเศษ: สามารถมี init statement ก่อน condition ได้ ตัวแปรที่ประกาศใน init statement มี scope แค่ภายใน if block นั้น:

// รูปแบบปกติ
if x > 10 {
fmt.Println("big")
}
// พร้อม init statement — err มี scope แค่ใน if/else block
if err := doSomething(); err != nil {
fmt.Println("error:", err)
}

Pattern นี้ใช้บ่อยมากกับ error handling ใน Go เพราะ scope ของ err ถูกจำกัดให้แคบที่สุด

Go มี loop keyword เดียวคือ for ซึ่งรองรับสามรูปแบบ:

รูปแบบที่ 1: C-style (init; condition; post)

for i := 0; i < 5; i++ {
fmt.Println(i)
}

รูปแบบที่ 2: Condition-only (เทียบเท่า while)

n := 1
for n < 100 {
n *= 2
}

รูปแบบที่ 3: Infinite loop

for {
// ออกด้วย break หรือ return
if done {
break
}
}

for range ใช้สำหรับ iterate ผ่าน collections:

// range over slice: ได้ index และ value
nums := []int{10, 20, 30}
for i, v := range nums {
fmt.Println(i, v)
}
// ต้องการเฉพาะ value: ใช้ _ สำหรับ index
for _, v := range nums {
fmt.Println(v)
}
// range over string: ได้ rune (Unicode code point) ไม่ใช่ byte
for i, r := range "Go!" {
fmt.Printf("%d: %c\n", i, r)
}

range ยังใช้กับ map (key, value) และ channel (value จนกว่าปิด) ได้ด้วย

switch ใน Go ไม่มี automatic fallthrough ต่างจาก C/Java ทุก case หยุดโดยอัตโนมัติ ถ้าต้องการ fallthrough ต้องระบุ fallthrough keyword อย่างชัดเจน:

day := "Monday"
switch day {
case "Saturday", "Sunday": // หลาย value ในหนึ่ง case ได้
fmt.Println("weekend")
case "Monday":
fmt.Println("start of week")
default:
fmt.Println("weekday")
}

Switch-true pattern ใช้แทน if-else chain ที่ซับซ้อน:

switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
case score >= 70:
fmt.Println("C")
default:
fmt.Println("F")
}

เมื่อ switch ไม่มี expression (switch บน true) แต่ละ case เป็น boolean expression ที่ evaluate ตามลำดับ

package main
import "fmt"
func classify(n int) string {
switch {
case n < 0:
return "negative"
case n == 0:
return "zero"
case n < 10:
return "small"
default:
return "large"
}
}
func main() {
// C-style for loop
fmt.Println("-- C-style loop --")
for i := 1; i <= 3; i++ {
fmt.Println(i)
}
// Condition-only loop (while equivalent)
fmt.Println("-- condition-only loop --")
n := 1
for n < 10 {
n *= 2
}
fmt.Println("n =", n)
// for range over slice
fmt.Println("-- range over slice --")
fruits := []string{"apple", "banana", "cherry"}
for i, v := range fruits {
fmt.Println(i, v)
}
// switch
fmt.Println("-- switch classify --")
for _, val := range []int{-5, 0, 7, 42} {
fmt.Println(val, "->", classify(val))
}
// if with init statement
fmt.Println("-- if with init --")
if x := 16; x%2 == 0 {
fmt.Println(x, "is even")
} else {
fmt.Println(x, "is odd")
}
}
สิ่งที่ได้ประโยชน์ต้นทุน
for keyword เดียวเรียนรู้น้อยกว่า, code สม่ำเสมอต้องจำรูปแบบสามแบบ (C-style, condition-only, infinite)
for rangeอ่านง่าย ไม่ต้องจัดการ index เองrange copy value — mutation ใน loop body ไม่กระทบ original slice
switch ไม่มี fallthrough อัตโนมัติลด bug จาก forgotten breakต้องเขียน fallthrough ชัดเจนถ้าต้องการ (rare)
if init statementscope ของ err แคบที่สุดsyntax แปลกสำหรับ developer ที่มาจากภาษาอื่น
  • defer ช้า”defer มี overhead น้อยมาก (nanosecond range) ใช้ได้สบายใน production code สำหรับ cleanup
  • range copy ทั้ง slice” — range copy เฉพาะ value ของแต่ละ element เท่านั้น ไม่ copy ทั้ง slice และ slice header ก็ถูก evaluate แค่ครั้งเดียวตอนเริ่ม loop
  • “Go for loop ต้องใช้ semicolon เหมือน C”for i := 0; i < n; i++ ใช้ semicolon แต่ for range และ condition-only loop ไม่ต้องใช้

💡 ตัวอย่างจากของจริง

Prometheus ใช้ for range ใน metric collection loop — iterate ผ่าน target หลายพัน target อย่างมีประสิทธิภาพ

Docker ใช้ defer ในการปิด file handle และ release lock เพื่อป้องกัน resource leak ใน container lifecycle management

อะไรคือ Go equivalent ของ while loop ในภาษาอื่น?
ใน Go switch statement เมื่อ case ตรงกันและไม่มี fallthrough keyword จะเกิดอะไร?
ตัวแปรที่ประกาศใน if init statement มี scope อย่างไร?
เมื่อ range over string ด้วย `for i, r := range s` ตัวแปร r มี type อะไร?