DEV Community

Weerasak Chongnguluam
Weerasak Chongnguluam

Posted on

Go: nil value comparison

#go

เนื่องจากค่า nil ใน Go สามารถเป็นค่าที่กำหนดให้กับหลาย type ได้ ดังนั้นการเปรียบเทียบค่า nil ด้วย operator == และ != จึงขึ้นอยู่กับ type ด้วยเช่นกัน ถ้าเราเขียนโค้ดแบบนี้

package main

import (
    "fmt"
)

func main() {   
    fmt.Println(nil == nil)
    fmt.Println(nil != nil)
}
Enter fullscreen mode Exit fullscreen mode

แล้ว compile ดูเราจะเจอ compile error แบบนี้

# command-line-arguments
./main.go:14:18: invalid operation: nil == nil (operator == not defined on nil)
./main.go:15:18: invalid operation: nil != nil (operator != not defined on nil)
Enter fullscreen mode Exit fullscreen mode

นั่นคือโดยตัวค่า nil เองนั้นเปรียบเทียบกันไม่ได้ เพราะว่าถ้าเราจับ nil ไปเปรียบเทียบตัว compiler จะไม่รู้ type ของ nil เลย

ทีนี้ type ใน Go ที่สามารถมีค่า nil ได้ประกอบไปด้วย

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

ทีนี้ถ้าผมเขียนโค้ดแบบนี้เพื่อทดสอบการเปรียบเทียบค่า nil ของแต่ละ type ด้านบน เริ่มจาก pointer

package main

import (
    "fmt"
)

func main() {
    var nilValue *string = nil
    fmt.Println(nilValue == nilValue)
    fmt.Println(nilValue != nilValue)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile จะ compile ได้และ run จะได้

> go run main.go 
true
false
Enter fullscreen mode Exit fullscreen mode

นั่นคือ pointer type เดียวกัน ที่มีค่า nil สามารถเปรียบเทียบกันได้

ต่อไปเป็น slice

package main

import (
    "fmt"
)

func main() {
    var nilValue []string = nil
    fmt.Println(nilValue == nilValue)
    fmt.Println(nilValue != nilValue)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile จะ error แบบนี้

> go run main.go 
# command-line-arguments
./main.go:9:23: invalid operation: nilValue == nilValue (slice can only be compared to nil)
./main.go:10:23: invalid operation: nilValue != nilValue (slice can only be compared to nil)
Enter fullscreen mode Exit fullscreen mode

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

package main

import (
    "fmt"
)

func main() {
    var nilValue []string = nil
    fmt.Println(nilValue == nil)
    fmt.Println(nilValue != nil)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile และ run แล้วจะได้

> go run main.go 
true
false
Enter fullscreen mode Exit fullscreen mode

ต่อไปเป็น map

package main

import (
    "fmt"
)

func main() {
    var nilValue map[string]string = nil
    fmt.Println(nilValue == nilValue)
    fmt.Println(nilValue != nilValue)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile จะ error แบบนี้

> go run main.go 
# command-line-arguments
./main.go:9:23: invalid operation: nilValue == nilValue (map can only be compared to nil)
./main.go:10:23: invalid operation: nilValue != nilValue (map can only be compared to nil)
Enter fullscreen mode Exit fullscreen mode

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

package main

import (
    "fmt"
)

func main() {
    var nilValue map[string]string = nil
    fmt.Println(nilValue == nil)
    fmt.Println(nilValue != nil)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile และ run แล้วจะได้

> go run main.go 
true
false
Enter fullscreen mode Exit fullscreen mode

ต่อไปเป็น channel

package main

import (
    "fmt"
)

func main() {
    var nilValue chan string = nil
    fmt.Println(nilValue == nilValue)
    fmt.Println(nilValue != nilValue)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile และ run แล้วจะได้

> go run main.go 
true
false
Enter fullscreen mode Exit fullscreen mode

นั่นคือ channel type เดียวกัน ที่มีค่า nil สามารถเปรียบเทียบกันได้

ต่อไปเป็น function

package main

import (
    "fmt"
)

func main() {
    var nilValue func() = nil
    fmt.Println(nilValue == nilValue)
    fmt.Println(nilValue != nilValue)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile จะ error แบบนี้

> go run main.go 
# command-line-arguments
./main.go:9:23: invalid operation: nilValue == nilValue (func can only be compared to nil)
./main.go:10:23: invalid operation: nilValue != nilValue (func can only be compared to nil)
Enter fullscreen mode Exit fullscreen mode

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

package main

import (
    "fmt"
)

func main() {
    var nilValue func() = nil
    fmt.Println(nilValue == nil)
    fmt.Println(nilValue != nil)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile และ run แล้วจะได้

> go run main.go 
true
false
Enter fullscreen mode Exit fullscreen mode

ต่อไปเป็น interface

package main

import (
    "fmt"
)

func main() {
    var nilValue interface{} = nil
    fmt.Println(nilValue == nilValue)
    fmt.Println(nilValue != nilValue)
}
Enter fullscreen mode Exit fullscreen mode

เมื่อ compile และ run แล้วจะได้

> go run main.go 
true
false
Enter fullscreen mode Exit fullscreen mode

นั่นคือ interface type เดียวกัน ที่มีค่า nil สามารถเปรียบเทียบกันได้

ถ้าจะสรุปก็คือ จะมีทั้งตัวแปรของ type ที่สามารถมีค่า nil แล้วสามารถเอาตัวแปรไปเปรียบเทียบกับตัวแปรอื่นที่มีค่า nil ได้ กับ อีกแบบคือเปรียบเทียบได้แต่กับ nil โดยตรง เปรียบเทียบกับตัวแปรอื่นไม่ได้

type ที่สามารถเปรียบเทียบตัวแปรที่มีค่า nil กับตัวแปรอื่นได้ ได้แก่

  • pointer
  • interface
  • channel

type ที่สามารถเปรียบเทียบตัวแปรที่มีค่า nil กับ nil โดยตรงได้เท่านั้นได้แก่

  • slice
  • map
  • function

สุดท้ายอีกเรื่องสำหรับ nil คือ nil ไม่ใช่ keyword ใน Go แต่เป็นชื่อที่ถูกกำหนดค่าเริ่มต้นเป็นค่าพิเศษค่านึงมาแล้ว ดังนั้น nil จึงสามารถถูกเอาไปใช้เป็นชื่อของตัวแปรได้ เช่นเราสามารถเขียนโค้ดนี้แล้ว compile และ run ได้

package main

import (
    "fmt"
)

func main() {
    nil := 0
    fmt.Println(nil == nil)
    fmt.Println(nil != nil)
}
Enter fullscreen mode Exit fullscreen mode

แต่อย่างไรก็ตาม อย่าเขียนแบบนี้ในโปรแกรมของเราเลยจะดีกว่า เพราะจะชวนให้เพื่อนๆในทีด่าได้ง่ายๆ :D

Top comments (0)