Structs
การนิยาม struct
หัวข้อที่มีชื่อว่า “การนิยาม struct”Struct คือการรวม field ที่เกี่ยวข้องกันไว้ภายใต้ชื่อ type เดียว แต่ละ field มีชื่อและ type:
type Point struct { X, Y int}
type User struct { Name string Email string Age int}Field ที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่จะถูก export (มองเห็นได้จากภายนอก package) ส่วน field ที่ขึ้นต้นด้วยตัวพิมพ์เล็กจะเป็น unexported
Struct literals
หัวข้อที่มีชื่อว่า “Struct literals”คุณสามารถสร้างค่า struct ได้สองแบบ:
// Positional — ต้องเรียงตามลำดับ field ที่ประกาศไว้p := Point{3, 4}
// Named fields — ลำดับไม่สำคัญ; field ที่ไม่ได้กำหนดจะได้ zero valueรูปแบบ named-field เป็นที่นิยมมากกว่า เพราะอ่านง่ายและทนต่อการเปลี่ยนลำดับ field
การเข้าถึง field
หัวข้อที่มีชื่อว่า “การเข้าถึง field”ใช้จุด (dot) เพื่ออ่านหรือเขียน field:
fmt.Println(u.Name) // อ่านu.Age = 30 // เขียนAnonymous structs
หัวข้อที่มีชื่อว่า “Anonymous structs”สำหรับ shape ที่ใช้ครั้งเดียว — configuration, test fixture, หรือการจัดกลุ่มภายใน — คุณสามารถนิยาม struct type แบบ inline โดยไม่ตั้งชื่อ:
cfg := struct { Host string Port int}{Host: "localhost", Port: 8080}การเปรียบเทียบ struct
หัวข้อที่มีชื่อว่า “การเปรียบเทียบ struct”ค่า struct สองค่าสามารถเปรียบเทียบด้วย == ได้ ถ้า ทุก field เป็น comparable type การเปรียบเทียบจะทำทีละ field:
a := Point{1, 2}b := Point{1, 2}fmt.Println(a == b) // trueStruct ที่มี field เป็น slice, map, หรือ function ไม่สามารถ เปรียบเทียบด้วย ==
Struct embedding (composition)
หัวข้อที่มีชื่อว่า “Struct embedding (composition)”Go ไม่มี class inheritance แต่ใช้การ embed struct type หนึ่งไว้ภายในอีก struct เพื่อ promote field และ method:
type Address struct { City string Country string}
type Person struct { Name string Age int Address // embedded — City และ Country ถูก promote}หลังจาก embed แล้ว person.City คือ shorthand ของ person.Address.City นี่คือ composition ไม่ใช่ inheritance — embedded type ไม่รู้ว่าตัวเองถูก embed และ outer type ไม่ใช่ subtype ของ inner type
package main
import "fmt"
type Address struct { City string Country string}
type Person struct { Name string Age int Address // embedded struct}
func main() { // Named-field literal p1 := Person{ Name: "Alice", Age: 30, Address: Address{City: "Bangkok", Country: "Thailand"}, } fmt.Println(p1.Name, p1.Age) fmt.Println(p1.City) // promoted from Address fmt.Println(p1.Country) // promoted from Address
// Struct copy: p2 is an independent value p2 := p1 p2.Name = "Bob" fmt.Println(p1.Name, p2.Name) // Alice Bob
// Anonymous struct cfg := struct { Host string Port int }{Host: "localhost", Port: 8080} fmt.Printf("%s:%d\n", cfg.Host, cfg.Port)
// Struct comparison (all fields comparable) a1 := Address{City: "Bangkok", Country: "Thailand"} a2 := Address{City: "Bangkok", Country: "Thailand"} fmt.Println(a1 == a2) // true}Loading Go runtime (first run only, ~8 MB)…
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| สิ่งที่ได้ | ประโยชน์ | ต้นทุน |
|---|---|---|
| value semantics | predictable behavior, ไม่มี aliasing | copy cost สำหรับ struct ขนาดใหญ่ |
| embedding (composition) | reuse field และ method โดยไม่ต้อง inheritance | field shadowing อาจสับสนเมื่อ embedded type มี field ชื่อเดียวกัน |
| struct tags | metadata สำหรับ JSON/DB/validation แบบ declarative | runtime-only — ไม่มี compile-time check ว่า tag ถูกต้อง |
| exported vs unexported fields | control API surface ได้ชัดเจน | refactoring ที่ expose field ใหม่เป็น breaking change |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- struct embedding = inheritance — Go ไม่มี inheritance —
Personที่ embedAddressไม่ใช่ subtype ของAddress - struct copy เสมอแพง — struct เล็ก (2-4 fields) copy เร็วกว่าการใช้ pointer ที่ต้อง dereference และอาจทำให้ object escape to heap
- unexported field = private ใน type — unexported หมายถึง package-level visibility — code ใน package เดียวกันเข้าถึงได้ทั้งหมด
- struct tags มีผลตอน compile — struct tags เป็น string literal ที่ reflection อ่านตอน runtime — typo ใน tag จะไม่มี compile error
💡 ตัวอย่างจากของจริง
Kubernetes ใช้ struct ลึกหลายชั้น เช่น
PodSpecembedContainer,Volume,Affinity— struct tags ควบคุม JSON serialization ทั้งหมดgorm (Go ORM) ใช้ struct tags เพื่อ map struct field ไปยัง database column —
gorm:"primaryKey"กำหนด behavior ตอน runtime