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

Functions

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
}

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 สุดท้าย

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 ยาวเพราะทำให้อ่านสับสน

ใช้ ...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 := 0
defer fmt.Println(i) // บันทึกค่า 0 ณ ตรงนี้
i = 100
// เมื่อ function return จะ print "0" ไม่ใช่ "100"

หลาย defer ในฟังก์ชันเดียวจะ execute แบบ LIFO (last in, first out)

Functions ใน Go เป็น first-class values — สามารถ assign ให้ตัวแปร, ส่งเป็น argument, หรือ return จาก function ได้:

// Function เป็น value ใน variable
double := func(x int) int { return x * 2 }
fmt.Println(double(5)) // 10
// Function เป็น parameter
apply := func(f func(int) int, x int) int { return f(x) }
fmt.Println(apply(double, 3)) // 6

Closure คือ function ที่ “จำ” ตัวแปรจาก enclosing scope:

func makeCounter() func() int {
count := 0
return func() int {
count++ // capture ตัวแปร count จาก makeCounter
return count
}
}
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2 — state ถูกเก็บไว้ใน closure
package main
import (
"errors"
"fmt"
)
// Multiple return values
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Variadic
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// Closure
func counter() func() int {
n := 0
return func() int {
n++
return n
}
}
// defer demo
func 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()
}
สิ่งที่ได้ประโยชน์ต้นทุน
multiple return valuesexplicit error handling ไม่มี hidden exceptioncaller ต้องตรวจทุก error — verbose กว่า try/catch
named return valuesdocumentation ใน signature, bare return ได้bare return ใน function ยาวทำให้อ่านสับสน
defercleanup code อยู่ใกล้กับ resource acquisitionarguments evaluate ทันที, LIFO order อาจ surprise
closuresencapsulate 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 ทำให้ระบบ resilient

etcd ใช้ closure ใน Raft consensus implementation เพื่อ capture election state โดยไม่ต้องใช้ global variable

ใน Go idiomatic pattern ใดที่ function ใช้เมื่ออาจล้มเหลว?
ถ้าประกาศ defer สามครั้งในลำดับ A, B, C จะ execute ในลำดับใด?
Closure ใน Go จำตัวแปรจาก enclosing scope อย่างไร?
จะส่ง slice เข้า variadic function เป็น arguments แยกกันได้อย่างไร?