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

Type Assertions

เมื่อ value ถูกเก็บใน interface variable, Go เก็บสองสิ่งไว้ภายใน: pointer ไปยัง concrete type descriptor และ pointer ไปยัง concrete value type assertion ดึง concrete value กลับออกมา

var s Shape = Circle{Radius: 5}
c := s.(Circle) // assert ว่า s เก็บ Circle
fmt.Println(c.Radius)

ถ้า s ไม่ได้เก็บ Circle นี่จะ panic ตอน runtime ใช้ form นี้เฉพาะเมื่อคุณแน่ใจใน dynamic type — เช่น ทันทีหลัง type check

variant ที่ปลอดภัยคืน boolean แทนการ panic:

c, ok := s.(Circle)
if ok {
fmt.Println("it's a circle with radius", c.Radius)
} else {
fmt.Println("not a circle")
}

ok เป็น true เมื่อ assertion สำเร็จ, false มิเช่นนั้น c เป็น zero value ของ Circle เมื่อ ok เป็น false ควรใช้ comma-ok form เสมอเมื่อ dynamic type ไม่แน่นอน

เมื่อต้องการ branch ตาม types หลายแบบ type switch สะอาดกว่าการเชื่อม if ok หลาย ๆ อัน:

switch v := x.(type) {
case Circle:
fmt.Println("circle", v.Radius)
case Rect:
fmt.Println("rect", v.W, v.H)
default:
fmt.Printf("unknown: %T\n", v)
}

ภายใน case แต่ละอัน v เป็น concrete type แล้ว — ไม่ต้อง assertion เพิ่มเติม default case ทำงานเมื่อไม่มี type ที่ระบุตรงกัน

คุณยัง assert จาก interface หนึ่งไปยังอีก interface ได้:

var w io.Writer = os.Stdout
if rc, ok := w.(io.ReadWriter); ok {
// os.Stdout implements io.ReadWriter
_ = rc
}

นี่คือวิธีที่โค้ด standard library ตรวจสอบ optional capabilities (เช่น ว่า Writer รองรับ Seek ด้วยหรือไม่)

package main
import "fmt"
type Animal interface {
Sound() string
}
type Dog struct{ Name string }
type Cat struct{ Name string }
func (d Dog) Sound() string { return "Woof" }
func (c Cat) Sound() string { return "Meow" }
func identify(a Animal) {
// comma-ok form — safe
if d, ok := a.(Dog); ok {
fmt.Printf("Dog named %s says %s\n", d.Name, d.Sound())
return
}
// type switch for multiple cases
switch v := a.(type) {
case Cat:
fmt.Printf("Cat named %s says %s\n", v.Name, v.Sound())
default:
fmt.Printf("Unknown animal: %T\n", v)
}
}
func main() {
animals := []Animal{Dog{Name: "Rex"}, Cat{Name: "Luna"}}
for _, a := range animals {
identify(a)
}
}
สิ่งที่ได้ประโยชน์ต้นทุน
type assertion v, ok := i.(T)ปลอดภัย ไม่ panicต้องตรวจสอบ ok ทุกครั้ง
type assertion v := i.(T)กระชับpanic ถ้า dynamic type ไม่ตรง
type switchhandle หลาย type ได้ชัดเจนverbose กว่า single assertion
reflect.TypeOfintrospect type ณ runtimeoverhead สูง, ใช้เฉพาะจำเป็น
  • type assertion เสมอปลอดภัย — ไม่ใช้ comma-ok form จะ panic ทันทีถ้า type ไม่ตรง
  • type switch เหมือน switch ทั่วไป — type switch ตรวจ dynamic type ของ interface ไม่ใช่ value
  • type assertion = type conversion — conversion เปลี่ยน type representation, assertion ตรวจว่า interface เก็บ concrete type นั้นจริงหรือไม่

💡 ตัวอย่างจากของจริง

encoding/json: Unmarshal ใช้ type assertion เพื่อ handle []interface\{\} และ map[string]interface\{\} เมื่อ JSON structure ไม่รู้ล่วงหน้า

net/http: middleware ตรวจว่า ResponseWriter implement http.Flusher หรือ http.Hijacker ด้วย type assertion เพื่อ enable streaming หรือ WebSocket upgrade

`v, ok := x.(T)` ทำอะไรเมื่อ x ไม่ได้เก็บ value ของ type T?
ภายใน type-switch case `case Rect:` type ของ switched variable `v` คืออะไร?
ควรใช้ form ใดเมื่อคุณไม่แน่ใจใน dynamic type?
`%T` ใน format string พิมพ์อะไร?