interface 与 nil

nil interface 与 nil 比较的坑

package main

import "fmt"

type Cat interface {
  Meow()
}

type Tabby struct {}
func (*Tabby) Meow() { fmt.Println("meow") }

func GetACat() Cat {
  var myTabby *Tabby = nil
  return myTabby
}

func main() {
  c := GetACat();
  if c == nil {
    fmt.Println("It is nil")
  } else {
    fmt.Printf("It is not nil. %v, %T", c, c)
  }
}

这段代码会输出 It is not nil. <nil>, *main.Tabby。 因为 interface 的底层结构是 <类型, 值> 的二元组。Golang 是传值的。所以这里的变量 c 的值是 nil,但是类型是 *Tabby。 interface 只有当类型和值都是 nil,即 <nil, nil> 时,才等于 nil。其他情况都不等于 nil。

所以为了避免这类判断的问题,可以在函数返回时,判断带有 nil 的指针就直接 return nil。例如改成下面的样子,

package main

import "fmt"

type Cat interface {
  Meow()
}

type Tabby struct {}
func (*Tabby) Meow() { fmt.Println("meow") }

func GetACat() Cat {
  var myTabby *Tabby = nil
  if myTabby == nil {
    return nil
  }
  return myTabby
}

func main() {
  c := GetACat();
  if c == nil {
    fmt.Println("It is nil")
  } else {
    fmt.Printf("It is not nil. %v, %T", c, c)
  }
}

另外要注意的是,不要用 nil 来表示操作成功或失败,应该利用函数的多值返回和 error 对象,来让调用者知道操作成功或失败。

如何判断 nil interface

参考这个例子里的三个函数 isNilisNilisNilBetter