เรียน Go ภาษาง่าย 📘

🪞

เจาะลึก Reflection + Unsafe + Cgo + ของทดลองใหม่ ฉบับภาษาง่าย

reflect, unsafe, cgo — รู้ไว้แต่เลี่ยงได้เลี่ยง

7 บท
10 ข้อสอบ
1. หลักคิด: อย่าใช้ถ้าเลี่ยงได้
  • งานทั่วไปใช้ generics/interface พอแล้ว เร็วกว่า อ่านง่ายกว่า ปลอดภัยกว่า
  • reflect ช้า + เสีย type safety, unsafe พังได้ทั้งโปรแกรมถ้า Go เปลี่ยนเวอร์ชัน, cgo build ยาก ไฟล์ใหญ่
  • ถ้าต้องใช้ให้ขังไว้ใน lib เล็กๆ + เทสต์เยอะๆ ไม่ใช่โปรยทั้งโปรเจกต์
✏️ แบบทดสอบท้ายบท
แบบทดสอบท้ายบท
0/2


2. reflect ส่องของตอนรัน

เหมาะกับงาน framework (แปลง struct <-> JSON/DB อัตโนมัติ):

go
import "reflect"
t := reflect.TypeOf(User{})
for i := 0; i < t.NumField(); i++ {
    f := t.Field(i)
    fmt.Println(f.Name, f.Tag.Get("json")) // อ่าน tag `json:"name"`
}
v := reflect.ValueOf(&u).Elem()
v.FieldByName("Name").SetString("Ann")

ทริก: ถ้าเรียกบ่อยให้ cache Type/Field ไว้ อย่า reflect ทุกรอบ (ช้า) และถ้าใช้ JSON ใหม่ json/v2 + jsontext (1.27) อาจไม่ต้อง reflect เองแล้ว

✏️ แบบทดสอบท้ายบท
แบบทดสอบท้ายบท
0/2


3. unsafe แหกกฎ (พร้อมคำเตือน go fix)

ตัวอย่างคลาสสิก: แปลง string<->[]byte ไม่ก็อป (เร็วแต่เสี่ยง):

go
import "unsafe"
// เร็วเพราะไม่ก็อป แต่ถ้าแก้ bytes ต้นฉบับ string จะเปลี่ยนด้วย (แหกสัญญา immutable)
b := unsafe.Slice(unsafe.StringData(s), len(s))

กฎ:

  • อย่าเก็บ pointer จาก unsafe ข้าม GC รอบโดยไม่มีตัวจับ (หลุดได้)
  • รัน go vet + go fix (มี unsafefuncs เตือนจุดเสี่ยงใน 1.26-1.27) ก่อนส่งเสมอ
  • อัป Go แล้วเทสต์ใหม่ทุกครั้ง ของที่เคยรอดอาจพังในเวอร์ชันใหม่
✏️ แบบทดสอบท้ายบท
แบบทดสอบท้ายบท
0/2


4. cgo เรียก C (เร็วขึ้น 30% ใน 1.26)
go
// #include <stdio.h>
// #include <stdlib.h>
import "C"
import "unsafe"
cs := C.CString("hello")
defer C.free(unsafe.Pointer(cs))
C.puts(cs)
  • 1.26 ลด overhead cgo ~30% + สุ่ม heap กันเจาะ (ปิดได้ด้วย GOEXPERIMENT=norandomizedheapbase64 เฉพาะตอนไล่บั๊กสาย cgo)
  • ถ้าไม่ใช้ C ให้ build CGO_ENABLED=0 ไปเลย ไฟล์เดียวจบ deploy ง่าย
  • ข้ามภาษาให้ copy ข้อมูลผ่าน C boundary อย่าส่ง pointer Go ให้ C เก็บยาว (GC ย้ายได้)
✏️ แบบทดสอบท้ายบท
แบบทดสอบท้ายบท
0/2


5. ของทดลอง 1.26: simd secret goroutineleak

ยังเป็น experimental ต้อง opt-in แต่อยากให้รู้ชื่อ (เดี๋ยวเป็นตัวจริงในอนาคต):

  • simd/archsimd — สั่ง CPU ทำหลายเลขพร้อมกัน เหมาะสายภาพ/เสียง/AI
  • runtime/secret — ล้าง key ใน memory ให้เกลี้ยง กันหลุดไปอยู่ใน dump (สาย wallet/auth)
  • runtime/pprof goroutineleak — จับ goroutine ที่ลืมปิดแล้วรั่ว ใช้คู่กับ testing/synctest (1.27) เทสต์ concurrent ไม่ flaky
bash
go test -race ./...
go tool pprof -http=:8080 goroutine.out

อ้างอิง: Go 1.26 (simd, secret, goroutineleak, cgo -30%, randomized heap), Go 1.27 (synctest), go fix unsafefuncs

✏️ แบบทดสอบท้ายบท
แบบทดสอบท้ายบท
0/2


ภาคผนวก: ตัวอย่างเพิ่ม + Best Practices
ตัวอย่างเพิ่ม 1: ส่องชนิดตอนรัน
go
var age int = 30
t := reflect.TypeOf(age)
fmt.Println(t.Kind(), t.Name()) // int int
ตัวอย่างเพิ่ม 2: อ่าน struct tag (ที่ json ใช้)
go
type User struct {
    Name string `json:"name"`
}
f, _ := reflect.TypeOf(User{}).FieldByName("Name")
fmt.Println(f.Tag.Get("json")) // name
Best practices เช็กลิสต์ (เล่มนี้เน้น "อย่าใช้")
  • กฎข้อ 1: เลี่ยงได้เลี่ยง ใช้ generics/interface แทนก่อนเสมอ
  • unsafe ใช้แล้ว guarantee ของ GC หาย อัป Go อาจพัง — คอมเมนต์เหตุผลไว้ทุกจุด
  • ห้ามเก็บ unsafe.Pointer ข้าม GC cycle ยาวๆ
  • cgo ช้าตรงข้ามภาษา เรียกถี่ๆ ช้ากว่า pure Go (1.26 เร็วขึ้น 30% แต่ยังช้ากว่า)
  • รัน go fix unsafefuncs หลังอัปเวอร์ชัน
  • เทสต์บนทุก OS ที่จะ deploy (ขนาด pointer/behavior ต่างกันได้)
แบบฝึกหัด
  • เขียน dump(v any) พิมพ์ field + tag ของ struct ใดๆ
  • ลอง go vet กับโค้ด unsafe ดูว่าเตือนอะไร