interface和nil 比较。

参考解析

题目来源:畅天游

解答:

  1. func main() {
  2. var res1 []string
  3. if res1 == nil {
  4. fmt.Println("res1 is nil")
  5. }
  6. IsNil(res1)
  7. IsNil(nil)
  8. }
  9. func IsNil(i interface{}) {
  10. if i == nil {
  11. fmt.Println("i is nil")
  12. return
  13. }
  14. fmt.Println("i isn't nil")
  15. }
  16. // 运行结果
  17. //res1 is nil
  18. //i isn't nil
  19. //i is nil

按照理解应该都输出is nil,但是为什么不一样呢?

  • interface 不是单纯的值,而是分为类型和值。所以必须要类型和值同时都为 nil 的情况下,interface 的 nil 判断才会为 true。
  • 一个 interface{} 类型的变量包含了 2 个指针,一个指针指向值的类型,另外一个指针指向实际的值。在 Go 源码中 runtime 包下,我们可以找到runtime.eface的定义。
  1. type eface struct {
  2. _type *_type
  3. data unsafe.Pointer
  4. }
  5. type _type struct {
  6. size uintptr // type size
  7. ptrdata uintptr // size of memory prefix holding all pointers
  8. hash uint32 // hash of type; avoids computation in hash tables
  9. tflag tflag // extra type information flags
  10. align uint8 // alignment of variable with this type
  11. fieldalign uint8 // alignment of struct field with this type
  12. kind uint8 // enumeration for C
  13. alg *typeAlg // algorithm table
  14. gcdata *byte // garbage collection data
  15. str nameOff // string form
  16. ptrToThis typeOff // type for pointer to this type, may be zero
  17. }

从中可以看到 interface 变量之所以可以接收任何类型变量,是因为其本质是一个对象,并记录其类型和数据块的指针。

  • res1赋值给interface后,res1本质是值为nil,类型并不是nil,所以输出i isn't nil

  • nil类型与值都为nil,所以输出 i is nil

  • 如何避免?

    1)既然值为 nil 的具型变量赋值给空接口会出现如此莫名其妙的情况,我们不要这么做,赋值前先做判空处理,不为 nil 才赋给空接口;

    2)使用reflect.ValueOf().IsNil()来判断。不推荐这种做法,因为当空接口对应的具型是值类型,会 panic。