Type อะไรใน Go มีค่าเป็น nil ได้บ้าง

Go มีค่าค่านึงที่พิเศษกว่าคืออื่น นั่นคือ nil ซึ่งคำว่า nil นั้นจริงๆไม่ใช่ keyword ด้วย แต่เป็น predefined value ความไม่เหมือนค่าอื่นของมันมีดังต่อไปนี้

nil ไม่มี type

ใช่แล้วครับ nil เฉยๆเลย โดยที่ยังไม่กำหนดค่าให้ตัวแปรอื่น หรือยังไม่ convert เป็น type อื่น มันจะไม่มี type ทดสอบง่ายๆเช่น

fmt.Printf("%T", nil) // จะได้ <nil>

ถ้าเราใส่ nil เข้าไปตรงๆ Printf ด้วย %T จะเลือกปริ้น <nil> เพราะมันก็ไม่รู้ว่าเป็น type อะไร

เราไม่สามารถเขียนโค้ดแบบนี้ได้

fmt.Println(nil == nil)

จะ compile ไม่ผ่านเพราะ == จะเปรียบเทียบได้ก็ต่อเมื่อรู้ type เท่านั้น แต่ nil ตรงๆ ไม่สามารถรู้ type ของมันได้

nil เป็น zero value (default type) ของ 6 type ต่อไปนี้

  • pointer ของ type ใดๆ
  • slice
  • map
  • channel
  • function
  • interface

ก่อนจะไล่ดูทีละ type ขอพูดถึงเรื่องการเปรียบเทียบด้วย == ก่อนว่า การเปรียบเทียบใน Go สำหรับ 6 type นั้นเปรียบเทียบกันไม่ได้ เพราะมันเป็นคนละ type ต่อให้ค่าเริ่มต้นมันจะเป็น nil เหมือนกันก็ตาม ต่อไปมาไล่ดูคุณสมบัติของแต่ละ type เมื่อตัวแปรของมันเป็น nil กัน แต่เราเอาตัวแปรนั้นเทียบกับ nil เฉยๆได้เช่น

var p *int
if p == nil {
fmt.Println("p is nil")
}

ที่เป็นแบบนี้เพราะ operator == จะแปลง nil ให้เป็น type เดียวกันกับ p ก่อนนั่นเอง

nil pointer

ตัวแปร pointer ถ้าเป็น nil เราจะทำการ dereference ด้วยการเอา operator * ไปใส่หน้าตัวแปร pointer ไม่ได้ จะเกิด panic: runtime error: invalid memory address or nil pointer dereference ซึ่งก็เข้าใจได้ไม่ยาก เพราะมันเป็น nil แสดงว่าไม่ได้กำหนดค่า address ของตัวแปรอื่นให้กับมัน

nil slice

ตัวแปร slice ถ้าเป็น nil ค่าของ len และ cap มันจะเป็นศูนย์ทั้งคู่

var s []string
fmt.Println(len(s), cap(s)) // 0, 0

เราสามารถ append ของเข้า slice ที่เป็น nil ได้โดยที่ไม่จำเป็นต้อง make ก่อน เช่น

var s []string
s = append(s, "hello", "world")
fmt.Println(s) // [hello, world]

nil map

ตัวแปร map ถ้าเป็น nil ค่าของ len จะเป็นศูนย์

var m map[string]int
fmt.Println(len(m)) // 0

ถ้าเราพยายามเอาค่าใส่ใน map ที่เป็น nil จะเกิด panic: assignment to entry in nil map ขึ้น

ถ้าเราพยายามเอาค่าออกจาก map ที่เป็น nil จะเหมือนกับเอาค่า key ที่ไม่มีใน map ออกมา นั่นคือได้ค่า zero value ของ value type เช่น

var m map[string]int
fmt.Println(m["a"]) // 0

nil channel

ถ้าเราพยายามส่งค่าไปใน channel ที่เป็น nil มันจะ block ไปตลอด

ถ้าเราพยายามเอาค่าออกจาก channel ที่เป็นnil มันจะ block ไปตลอด เช่นกัน

ถ้าเราพยายาม close channel ที่เป็น nil จะเกิด panic: close of nil channel

nil function

ถ้าเราพยายามเรียกให้ตัวแปรที่เป็น function ที่มีค่า nil ทำงานจะเกิด panic เช่น

var f func()
f()
// panic: runtime error: invalid memory address or nil pointer dereference

nil interface

เนื่องจาก interface สามารถถูกกำหนดค่าโดยตัวแปร หรือ ค่าตรงๆของ type อื่นได้ แต่ interface ที่จะมีค่าเป็น nil นั่นคือ interface ที่ไม่ได้ถูกกำหนดค่า หรือ ถูกกำหนดค่าด้วยค่าคงที่ nil ที่เป็น predefined value เท่านั้น เช่น

var reader io.Reader // reader is nil value interface
var writer io.Writer
writer = nil // writer is nil value interface

ดังนั้นถ้าเกิดเราเอาตัวแปรของ type อื่น ที่ implement interface แต่มีค่าเป็น nil กำหนดให้ตัวแปรของ interface แม้ว่าค่าของ type นั้นจะเป็น nil แต่ค่าของตัวแปร interface จะไม่เป็น nil เพราะอย่างที่บอกตัวแปร interface จะเป็น nil เมื่อมันไม่มีการกำหนดค่า หรือเรากำหนด predefined nil ให้เท่านั้น ตัวอย่างเช่น

type MyError struct{}
func (*MyError) Error() string {
return "BANGGGG"
}
var mErr *MyError // mError is zeror value `nil` but type pointer
var err error // err is zero value `nil`
err = mErr // err is not nil
fmt.Println(err == nil) // false

นอกจากนั้น ถ้าเราใช้ switch กับ interface type assertion เราสามารถใส่ case nil สำหรับกรณีที่ interface ไม่ได้ถูกกำหนดค่าอะไรได้อีกด้วย เช่น

var err error
switch err.(type) {
case nil: fmt.Println("err is nil")
}

สรุป

nil สำหรับ Go เป็น value ที่ไม่มี type แต่ตัวแปรที่เรากำหนดค่า nil ให้มันได้มี 6 type ตามที่กล่าวไป และพฤติกรรมเวลามันมีค่า nil ก็แตกต่างกันไปตามที่บอก ที่ต้องระวังคือ nil interface เพราะมันกำหนดค่าได้หลาย type แต่จำง่ายๆ nil interface คือ interface ที่ไม่ได้กำหนดค่าอะไร (zero value) หรือ กำหนดค่า predefined nil เท่านั้น

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.