DEV Community

Gophernment Co
Gophernment Co

Posted on

Go 1.27 — Generic Methods, JSON v2, UUID ใน stdlib ฯลฯ

Go 1.27 — Generic Methods, JSON v2, UUID ใน stdlib ฯลฯ

⚠️ Go 1.27 ยังไม่ release นะทุกคน release notes ยังเป็น draft อยู่ คาดว่าจะออกจริงประมาณ สิงหาคม 2026
เราไปดูกันดีกว่าว่ารอบนี้มีอะไรน่าตื่นเต้นบ้าง

ส่วนตัวผมตื่นเต้นกับเวอร์ชันนี้มาก เพราะมีของที่รอมานานหลายอย่างโผล่มาใน draft นี้พร้อมกัน ทั้ง Generic Methods ที่คนเรียกร้องกันมาตั้งแต่ Go 1.18, JSON v2 ที่คุยกันมาเป็นปี ๆ, และ UUID ใน stdlib ที่หลายคน (รวมผม) รอมานานจนเกือบลืม

งั้นมาเริ่มกันเลย


🎯 Generic Methods — ของที่รอมา 4 ปี

เล่าเรื่องก่อน

ย้อนกลับไปตอน Go 1.18 (ปี 2022) — Go เพิ่งใส่ generics เข้ามาครั้งแรก1 ตอนนั้น community ดีใจกันใหญ่ แต่ก็มีเสียงบ่นปนมาด้วยว่า "เฮ้ย แล้ว generic methods ล่ะ?"

ปัญหามันเป็นแบบนี้...

สมมติเราเขียน Set[T] ขึ้นมาตัวนึง:

type Set[T comparable] struct {
    m map[T]struct{}
}
Enter fullscreen mode Exit fullscreen mode

แล้วเราอยากมี method Map — แปลงจาก Set[int] เป็น Set[string] — ใน Go 1.18-1.26 เราทำแบบนี้ไม่ได้:

// ❌ ทำไม่ได้ใน Go ≤ 1.26 — method ห้ามมี type parameter ตัวเอง
func (s *Set[T]) Map[U any](f func(T) U) *Set[U] {
    // compiler จะด่า "method must have no type parameters"
}
Enter fullscreen mode Exit fullscreen mode

ทำไม? เพราะตอน Go 1.18 ทีมออกแบบเลือกที่จะ implement แค่ "generic functions" ในระดับ package ก่อน — method ยังต้องเหมือนเดิม2 การจะทำให้ method มี type parameter ของตัวเองได้ มันต้องแก้ compiler, runtime, และ reflect ใหม่ยกใหญ่

จนมาถึง Go 1.27

ในที่สุด — method มี type parameter ของตัวเองได้แล้ว!

// ✅ Go 1.27 — ได้แล้วครับ!
func (s *Set[T]) Map[U any](f func(T) U) *Set[U] {
    result := &Set[U]{m: make(map[U]struct{})}
    for v := range s.m {
        result.m[f(v)] = struct{}{}
    }
    return result
}
Enter fullscreen mode Exit fullscreen mode

จะเกิดอะไรขึ้น? เรา chain method สวย ๆ ได้เลย:

scores := NewSet(10, 20, 30)

// แปลงเป็น string → filter → นับ — ในบรรทัดเดียว!
labels := scores.
    Map(func(n int) string { return fmt.Sprintf("score_%d", n) }).
    Filter(func(s string) bool { return len(s) > 7 })
Enter fullscreen mode Exit fullscreen mode

ก่อน Go 1.27: ต้องเขียนแบบนี้ — function กระจายอยู่ใน package เปล่า ๆ อ่านไม่รู้เรื่อง:

labels := FilterSet(MapSet(scores, func(n int) string { ... }),
    func(s string) bool { ... })
Enter fullscreen mode Exit fullscreen mode

ข้อจำกัดที่ยังมีอยู่: interface method ยังมี type parameter ไม่ได้ และ generic method ก็ implement interface ไม่ได้3

ทำไมถึงสำคัญ?

ลองนึกภาพเวลาคุณเขียน library ที่ return type เป็น generic container — Set[T], Result[T], Option[T] — คุณอยากให้คนใช้ chain operation ต่อได้ใช่ไหม? Generic methods ทำให้สิ่งนี้เป็นจริง

💡 Footnote: ทีม Go ใช้เวลานานกับฟีเจอร์นี้เพราะ philosophy ของ Go คือ "เรียบง่ายก่อน ถูกต้องเสมอ" การเพิ่ม generic methods ต้องทำให้แน่ใจว่ามันไม่ทำให้ type inference ช้าลง ไม่มี edge case แปลก ๆ และการคอมไพล์ยังเร็วเหมือนเดิม


📦 JSON v2 — encoding/json ภาคใหม่

ทำไมต้อง v2?

encoding/json ปัจจุบันอยู่กับเรามาตั้งแต่ Go 1.0 (ปี 2012) มันทำงานได้ดี แต่ปัญหาสะสมมาตลอด 13 ปี:

  1. Performance — ใช้ reflection เยอะ4 marshal/unmarshal struct ที่มี nested field เยอะ ๆ แล้วช้า
  2. API ไม่ยืดหยุ่น — อยาก customise marshaling? ต้อง implement json.Marshaler เองทั้งก้อน ไม่ใช่แค่บาง field
  3. Streaming ไม่สะดวก — ถ้าข้อมูล JSON มาเป็น streaming (เช่น WebSocket, Server-Sent Events) การ decode ด้วย json.Decoder ตอนนี้ยังมีข้อจำกัดหลายอย่าง
  4. Error messages — ถ้า json ไม่ตรง schema error message อ่านยาก — "unexpected token" แล้วจบ ไม่บอกว่าบรรทัดไหน field ไหน

มีอะไรใน v2

Go 1.27 เอา encoding/json/v2 เข้ามาใน stdlib — ยังเป็น experimental แต่มันคือรากฐานของ JSON handling ยุคใหม่

  • encoding/json/v2 — API ใหม่ที่ยืดหยุ่นกว่า, เร็วกว่า
  • encoding/json/jsontext — low-level tokenizer/encoder สำหรับคนที่อยากทำอะไร custom กับ JSON stream โดยตรง

รายละเอียดยังไม่เปิดเผยหมด เพราะ v2 ยังอยู่ระหว่างพัฒนา แต่ที่แน่ ๆ คือมันจะแก้ pain point ทั้ง 4 ข้อข้างบน

💡 สำหรับ junior dev: ถ้าตอนนี้คุณใช้ encoding/json อยู่ — ใช้ต่อไปได้เลย ไม่ต้องรีบเปลี่ยน v2 ยังเป็น experimental และ API อาจเปลี่ยนได้ระหว่างทาง


🆔 UUID ใน stdlib — ในที่สุด!

มันคืออะไร?

UUID (Universally Unique Identifier) คือ ID ที่สร้างแล้ว "ไม่ซ้ำ" กับใครในโลก — ใช้แทน auto-increment ID เวลาอยากให้ ID เดาจากภายนอกไม่ได้5

ก่อน Go 1.27 — คุณต้อง go get github.com/google/uuid (ดาวน์โหลด ~1.3M) หรือ gofrs/uuid — ซึ่งก็ดีนะ แต่คำถามคือ "ทำไมของพื้นฐานขนาดนี้ถึงไม่อยู่ใน stdlib?"

ใน Go 1.27

import "uuid"

// UUID v7 — timestamp-based, sort ได้ตามเวลา
id := uuid.NewV7()
fmt.Println(id.String())
// Output: 019309e2-29a3-7b2f-a4d5-80d44c4b3f1d
Enter fullscreen mode Exit fullscreen mode

ทำไมต้อง UUID v7? UUID ธรรมดา (v4) ใช้ random ล้วน ๆ — ทำให้ index ใน database B-tree6 กระจัดกระจาย UUID v7 เอา timestamp ใส่ไว้ข้างหน้า ทำให้ "sort ได้ตามเวลา" — ข้อนี้สำคัญมากสำหรับ performance database

เปรียบเทียบง่าย ๆ: UUID v4 = จับสลากแบบสุ่ม / UUID v7 = จับสลากแบบเรียงตามคิว


🧪 SIMD ใน Go — CPU มัลติทาสกิ้ง

SIMD คืออะไร?

สมมติคุณมีตัวเลข 8 ตัว แล้วอยากบวก 1 ให้ทุกตัว — ปกติ CPU ทำทีละตัว 8 รอบ

SIMD (Single Instruction, Multiple Data) คือการทำ "คำสั่งเดียว" กับ "ข้อมูลหลายตัวพร้อมกัน" — เหมือนเชฟที่หั่นผักทีเดียว 8 หัวแทนที่จะหั่นทีละหัว

// Concept (ยังไม่ใช่ syntax จริง)
// ปกติ: for i := range data { data[i] += 1 }  ← 8 รอบ
// SIMD:  data += [1,1,1,1,1,1,1,1]            ← รอบเดียว!
Enter fullscreen mode Exit fullscreen mode

Go 1.27 มี simd + simd/archsimd เป็น experimental package — ยังต้อง build ด้วย GOEXPERIMENT=simd

ใช้กับอะไรได้บ้าง:

  • Image processing (ปรับแสง, resize พร้อมกันหลาย pixel)
  • Machine learning inference
  • Cryptography (เข้ารหัสหลาย block พร้อมกัน)
  • Audio/Video encoding

💡 ต้องบอกว่า SIMD ใน Go ยังเด็กมาก — อย่าเพิ่งเอาไปใช้ production แต่มันเป็นการวางรากฐานที่สำคัญมาก


⚡ Runtime Improvements

Goroutine Leak Profile

goroutine7 รั่วคือฝันร้ายของ Go dev — มันใช้ memory ไปเรื่อย ๆ โดยไม่รู้ตัว

Go 1.27 เพิ่ม goroutine leak profile — ใช้กับ go tool pprof เพื่อดูว่า goroutine ตัวไหนที่เกิดแล้วไม่ยอมตาย

# ดู goroutine leak profile (คาดว่าใช้ประมาณนี้)
go tool pprof http://localhost:6060/debug/pprof/goroutine
Enter fullscreen mode Exit fullscreen mode

Faster Memory Allocation

รายละเอียดยังไม่เปิดเผยมาก แต่ทีม runtime บอกว่าปรับปรุง allocator ให้เร็วขึ้น — โดยเฉพาะเคส allocate ของเล็ก ๆ จำนวนมาก


🖥️ Ports

  • macOS (Darwin) — รองรับเวอร์ชันล่าสุด
  • PowerPC 64-bit (ppc64) — กลับมาอยู่ใน first-class port8 ใครใช้ IBM Power server ยิ้มได้

📊 สรุปให้

หมวด ฟีเจอร์ น่าตื่นเต้น? ใช้ production ได้เลย?
Language Generic Methods 🔥🔥🔥 ✅ (หลังออกจริง)
Language Struct Literal Keys
Language Function Type Inference
stdlib encoding/json/v2 🔥🔥 🧪 experimental
stdlib uuid 🔥
stdlib crypto/mldsa 🔥
stdlib simd 🔥🔥 🧪 experimental
Runtime Faster Allocation
Runtime Goroutine Leak Profile

Go 1.27 ยังอีกประมาณ 2 เดือนกว่าจะออกจริง — release notes อาจเปลี่ยนได้ ติดตามที่ go.dev/doc/go1.27


📚 Footnotes — อ่านเพิ่มเติมสำหรับคนอยากรู้ลึก


📚 Source: Go 1.27 Release Notes (Draft) — The Go Programming Language


  1. Go Generics (1.18): Go ใส่ type parameter เข้ามาในปี 2022 — ใช้ syntax func F[T any](t T) และ constraint แบบ [T interface{ ~int | ~float64 }] — ทำให้เขียน function ที่ทำงานกับหลาย type ได้โดยไม่ต้องใช้ interface{} + type assertion 

  2. ทำไม generic methods ถึงยาก: เพราะ Go method ทำงานผ่าน "method set" ซึ่งถูกกำหนดที่ compile time การเพิ่ม type parameter ให้ method ทำให้ method set กลายเป็น "generic" — ต้องแก้ทั้ง compiler (SSA generation), runtime (interface dispatch), และ reflect 

  3. Interface กับ generic method: ใจความคือ interface เป็น "สัญญา" ที่ concrete type ต้อง implement — ถ้า method มี type parameter มันก็เหมือน "สัญญาที่ยังไม่ได้ระบุประเภท" เลย implement interface ไม่ได้ 

  4. Reflection: encoding/json ใช้ reflect เพื่ออ่าน struct tags (json:"name") และ type ของ field — การใช้ reflect มี overhead เพราะต้อง resolve type ที่ runtime แทนที่จะทำตอน compile time 

  5. UUID vs Auto-Increment: Auto-increment ID (1, 2, 3, ...) ทำให้คนนอกเดาได้ว่ามี record กี่อัน และเดา ID ของ record ถัดไปได้ UUID แก้ปัญหานี้ 

  6. B-tree Index: index มาตรฐานใน database (PostgreSQL, MySQL) — ทำงานเร็วเมื่อข้อมูล "เรียงตามลำดับ" UUID v4 สุ่ม ทำให้ B-tree ต้องจัดเรียงบ่อย UUID v7 เรียงตามเวลา ทำให้ insert แล้ว index เป็นระเบียบ — performance ดีกว่า 

  7. Goroutine: lightweight thread ของ Go — สร้างได้เป็นแสน ๆ ตัวโดยใช้ memory น้อย แต่ถ้า goroutine เกิดแล้วไม่มีทางจบ (leak) มันจะกิน memory สะสมไปเรื่อย ๆ 

  8. First-class port: หมายถึง architecture ที่ได้รับการทดสอบแบบเต็มรูปแบบ CI/CD — มี binary prebuilt ให้บน go.dev/dl — ไม่ใช่แค่ "cross-compile ได้" 

Top comments (0)