Functions
Syntax พื้นฐานของ Function
หัวข้อที่มีชื่อว่า “Syntax พื้นฐานของ Function”Function ใน Go ประกาศด้วย keyword func ตามด้วยชื่อ, parameters, และ return types:
func add(x int, y int) int { return x + y}
// Parameters ที่มี type เดียวกันสามารถเขียนรวมได้func add(x, y int) int { return x + y}Multiple Return Values
หัวข้อที่มีชื่อว่า “Multiple Return Values”Go functions สามารถ return หลายค่าได้ ที่เป็น pattern มาตรฐานสำหรับ error handling:
func divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil}
result, err := divide(10, 3)if err != nil { log.Fatal(err)}Pattern (value, error) ถือเป็น idiomatic Go — แทบทุก function ที่อาจล้มเหลวจะ return error เป็น value สุดท้าย
Named Return Values
หัวข้อที่มีชื่อว่า “Named Return Values”Return values สามารถตั้งชื่อได้ ทำให้สามารถใช้ bare return ได้:
func minMax(nums []int) (min, max int) { min, max = nums[0], nums[0] for _, n := range nums[1:] { if n < min { min = n } if n > max { max = n } } return // return min, max โดยไม่ต้องระบุ}ใช้ named returns เพื่อความชัดเจนใน function สั้น ๆ หลีกเลี่ยงใน function ยาวเพราะทำให้อ่านสับสน
Variadic Functions
หัวข้อที่มีชื่อว่า “Variadic Functions”ใช้ ...T เพื่อรับ arguments จำนวนไม่จำกัดของ type เดียวกัน:
func sum(nums ...int) int { total := 0 for _, n := range nums { total += n } return total}
sum(1, 2, 3) // ส่งแต่ละตัวsum(1, 2, 3, 4, 5) // จำนวนเท่าไรก็ได้
existing := []int{1, 2, 3}sum(existing...) // spread slice ด้วย ...defer เลื่อนการเรียก function ออกไปจนกว่า function ที่ครอบอยู่จะ return เหมาะสำหรับ cleanup:
func readFile(path string) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() // จะถูกเรียกเมื่อ readFile return ไม่ว่าจะ return ปกติหรือ error // ... ใช้ f}Arguments ของ deferred call ถูก evaluate ทันที ณ จุดที่ defer statement ถูก execute ไม่ใช่ตอน call จริง:
i := 0defer fmt.Println(i) // บันทึกค่า 0 ณ ตรงนี้i = 100// เมื่อ function return จะ print "0" ไม่ใช่ "100"หลาย defer ในฟังก์ชันเดียวจะ execute แบบ LIFO (last in, first out)
Closures และ First-Class Functions
หัวข้อที่มีชื่อว่า “Closures และ First-Class Functions”Functions ใน Go เป็น first-class values — สามารถ assign ให้ตัวแปร, ส่งเป็น argument, หรือ return จาก function ได้:
// Function เป็น value ใน variabledouble := func(x int) int { return x * 2 }fmt.Println(double(5)) // 10
// Function เป็น parameterapply := func(f func(int) int, x int) int { return f(x) }fmt.Println(apply(double, 3)) // 6Closure คือ function ที่ “จำ” ตัวแปรจาก enclosing scope:
func makeCounter() func() int { count := 0 return func() int { count++ // capture ตัวแปร count จาก makeCounter return count }}
counter := makeCounter()fmt.Println(counter()) // 1fmt.Println(counter()) // 2 — state ถูกเก็บไว้ใน closurepackage main
import ( "errors" "fmt")
// Multiple return valuesfunc divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil}
// Variadicfunc sum(nums ...int) int { total := 0 for _, n := range nums { total += n } return total}
// Closurefunc counter() func() int { n := 0 return func() int { n++ return n }}
// defer demofunc deferDemo() { fmt.Println("deferDemo start") defer fmt.Println("deferred 1 (runs last)") defer fmt.Println("deferred 2 (runs second)") defer fmt.Println("deferred 3 (runs first)") fmt.Println("deferDemo end")}
func main() { // Multiple returns result, err := divide(10, 3) if err != nil { fmt.Println("error:", err) } else { fmt.Printf("10 / 3 = %.4f\n", result) }
_, err = divide(5, 0) if err != nil { fmt.Println("divide by zero:", err) }
// Variadic fmt.Println("sum(1..5) =", sum(1, 2, 3, 4, 5))
// Closure counter c := counter() fmt.Println("counter:", c(), c(), c())
// defer LIFO deferDemo()}Loading Go runtime (first run only, ~8 MB)…
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| สิ่งที่ได้ | ประโยชน์ | ต้นทุน |
|---|---|---|
| multiple return values | explicit error handling ไม่มี hidden exception | caller ต้องตรวจทุก error — verbose กว่า try/catch |
| named return values | documentation ใน signature, bare return ได้ | bare return ใน function ยาวทำให้อ่านสับสน |
defer | cleanup code อยู่ใกล้กับ resource acquisition | arguments evaluate ทันที, LIFO order อาจ surprise |
| closures | encapsulate state ได้กระชับ | captured variable มักถูก allocate บน heap (escape analysis) |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- “multiple return values ทำให้ performance แย่ลง” — Go return multiple values ผ่าน register หรือ stack โดยตรง ไม่มี heap allocation; แทบไม่มีผลต่อ performance
- “closure ทุกตัว escape ไปที่ heap” — compiler ทำ Escape Analysis และอาจ allocate closure บน stack ถ้าไม่ออกนอก scope
- “function ใน Go ไม่ใช่ first-class” — function ใน Go เป็น first-class value สามารถ assign, ส่งเป็น argument, หรือ return จาก function อื่นได้
💡 ตัวอย่างจากของจริง
Kubernetes ใช้ pattern
(result, error)ทุกที่ใน API server — caller บังคับตรวจ error ทำให้ระบบ resilientetcd ใช้ closure ใน Raft consensus implementation เพื่อ capture election state โดยไม่ต้องใช้ global variable