nil

不同的nil

nil
// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

// Type is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type int
pointerchannelfuncinterfacemapslice
(*int)(nil)(interface{})(nil)([]int)(nil)
(*B)(nil)(A)(a)*Bnil

 

特别的nil

尝试一下一行很简单的代码

var a = nil


 1 package main
 2 
 3 
 4 
 5 type StructB struct{}
 6 type StructA interface {}
 7 func main() {
 8     var e1 StructA  =   nil;
 9     var e2 * StructA  =   nil;
10     var e3 =   nil;
11     var e4 =   nil;
12 }
go build -o hello hello.go
# command-line-arguments
./hello.go:10:9: use of untyped nil
./hello.go:11:9: use of untyped nil

报错"use of untyped nil in variable declaration". 这很好理解,任何变量都应该有类型的,但是a的类型是什么呢,编译器百思不得其解,于是它生气了。哄一下应该没用,试着这样改一下就没问题了。

var a = (*int)(nil)

不过上文的错误信息中出现了一个特殊的nil,"untyped nil". 当我们直接写一个nil出来的时候,它是没有类型的。没有类型的nil虽然不能直接赋值给变量,但是可以与一些特定类型的变量进行比较,比如上面出现过的

var a *B
print(a == nil)
untyped nil

 

pointer

nil pointer就是一个没有指向任何值的指针,它的值是 0x0. 做个小实验

var a = (*int)(unsafe.Pointer(uintptr(0x0)))
print(a == nil)  //true

var a = []int{}
print(a==nil) //false 
type aa struct {
    ptr unsafe.Pointer
    len int
    cap int
}
aaa := (*aa)(unsafe.Pointer(&a))
aaa.ptr = nil
print(a==nil) //true

 

==============================================================================================================================

最近遇到一个由于 Golang Interface 底层实现,引发的线上 panic 问题,虽然根源在于赋值操作没有保护起来,却意外地发现了关于 interface 的一些有意思的底层细节。

假设我们现在有以下定义:

type Interface interface {
    Run()
}

type Implement struct {
    n int
}

func (i *Implement) Run() {
    fmt.Printf(i.n)
}
Interface*Implement
func main() {
    var i Interface
    fmt.Printf("%T\n", i) //<nil>
    i = &Implement{n: 1}
    fmt.Printf("%T\n", i) //*main.Implement

    var p *Implement
    fmt.Printf("%T\n", p) //*main.Implement
    p = &Implement{n: 1}
    fmt.Printf("%T\n", p) //*main.Implement
}

如果现在有这么一段代码:

func check(i Interface) {
    if i == nil {
        return
    }
    impl := i.(*Implement)
    fmt.Println(impl.n) //Invalid memory address or nil pointer dereference
}
impl.nimpl := i.(*Implement)impl.n
*ImplementInterfaceiface
type iface struct {
    tab  *itab
    data unsafe.Pointer
}

type itab struct {
    inter *interfacetype
    _type *_type
    hash  uint32 // copy of _type.hash. Used for type switches.
    _     [4]byte
    fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
tabdata.tab.data!= niliface
.tabimpl := i.(*Implement).dataimpl.n.data

 

 

interface nil 判断

这个已经说过,当一个interface的type和value都是nil的时候,这个interface才等于nil. 这真的是个坑人无数的golang陷阱

type A interface{}
type B struct{}
var a A = (*B)(nil)
print(a == nil) //false
a = nil
print(a == nil) //true

 

package main


import (
    "fmt"
    "reflect"
)


type StructB struct{}
func main() {
    var e  =  (*StructB)( nil);
    fmt.Println(reflect.TypeOf(e).Kind())
    fmt.Println(reflect.ValueOf(e))
    fmt.Println(reflect.TypeOf((interface{})(e)).Kind())
    fmt.Println(reflect.ValueOf((interface{})(e)))
}

 

./hello 
ptr
<nil>
ptr

 

package main


import (
    "fmt"
    "reflect"
)


type StructB struct{}
type StructA interface {}
func main() {
   // var e StructA  =  (*StructB)( nil);
    var e   =  (*StructB)( nil);
    fmt.Println(reflect.TypeOf(e).Kind())
    fmt.Println(reflect.ValueOf(e))
    fmt.Println(reflect.TypeOf((interface{})(e)).Kind())
    fmt.Println(reflect.ValueOf((interface{})(e)))
    fmt.Println(e == nil)
    e = nil
    fmt.Println(reflect.TypeOf(e).Kind())
    fmt.Println(reflect.ValueOf(e))
    fmt.Println(reflect.TypeOf((interface{})(e)).Kind())
    fmt.Println(reflect.ValueOf((interface{})(e)))
    fmt.Println(e == nil)
}

 

ptr
<nil>
ptr
<nil>
true
ptr
<nil>
ptr
<nil>
true

 

 

package main

import (
    "fmt"
    "reflect"
)


type StructB struct{}
type StructA interface {}
func main() {
    var e StructA  =  (*StructB)( nil);
    fmt.Println(reflect.TypeOf(e).Kind())
    fmt.Println(reflect.ValueOf(e))
    fmt.Println(reflect.TypeOf((interface{})(e)).Kind())
    fmt.Println(reflect.ValueOf((interface{})(e)))
    fmt.Println(e == nil)
    e = nil
    fmt.Println(reflect.TypeOf(e).Kind())
    fmt.Println(reflect.ValueOf(e))
    fmt.Println(reflect.TypeOf((interface{})(e)).Kind())
    fmt.Println(reflect.ValueOf((interface{})(e)))
    fmt.Println(e == nil)
}

 

ptr
<nil>
ptr
<nil>
false
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x98 pc=0x9ac88]

goroutine 1 [running]:
main.main()
        /root/go_learn/example.com/hello/hello.go:19 +0x358

 

package main

import (
    "fmt"
    "reflect"
)


type StructB struct{}
type StructA interface {}
func main() {
    var e StructA  =  (*StructB)( nil);
    fmt.Println(reflect.TypeOf(e).Kind())
    fmt.Println(reflect.ValueOf(e))
    fmt.Println(reflect.TypeOf((interface{})(e)).Kind())
    fmt.Println(reflect.ValueOf((interface{})(e)))
    fmt.Println(e == nil)
    e = nil
    //fmt.Println(reflect.TypeOf(e).Kind())
    //fmt.Println(reflect.ValueOf(e))
    //fmt.Println(reflect.TypeOf((interface{})(e)).Kind())
    //fmt.Println(reflect.ValueOf((interface{})(e)))
    fmt.Println(e == nil)
}

 

 

ptr
<nil>
ptr
<nil>
false
true

 

 

 

type A interface{}
type B struct{}
var a *B

print(a == nil)            //true
print(a == (*B)(nil))      //true
print((A)(a) == (*B)(nil)) //true

print((A)(a) == nil)       //false

 

 

/反射 是如何处理 匿名字段的?
package main


import (
    "fmt"
    "reflect"
)

type AInt int
type BInt AInt

func main() {
    var e interface{} = BInt(10)
    fmt.Println(reflect.TypeOf(e).Kind())
}

 

./hello 
int

 

package main


import (
    "fmt"
    "reflect"
)


type StructB struct{}
func main() {
    var e *StructB
    fmt.Println(reflect.TypeOf(e).Kind())
    fmt.Println(reflect.ValueOf(e))
    fmt.Println(reflect.TypeOf((interface{})(e)).Kind())
    fmt.Println(reflect.ValueOf((interface{})(e)))
}

 

./hello 
ptr
<nil>
ptr
<nil>

 

interface空指针不为nil

当把一个空指针对象赋值给一个interface后,再判断!= nil就不再成立了
代码如下

package main

import "fmt"

type Person interface {
	Name() string
}

type ChenQiongHe struct {
}

func (t *ChenQiongHe) Name() string {
	return "雪山飞猪"
}

func main() {
	var test *ChenQiongHe
	if test == nil {
		fmt.Println("test == nil")
	} else {
		fmt.Println("test != nil")
	}
	//将空指针赋值给接口
	var person Person = test
	if person == nil {
		fmt.Print("person == nil")
	} else {
		fmt.Print("person != nil")
	}
}

运行结果

test == nil
person != nil

test本来是nil,赋值给person后居然不能再用nil判断了

解决方法

使用reflect包的IsNil判断,封装为一个能用方法

func IsNil(i interface{}) bool {
	vi := reflect.ValueOf(i)
	if vi.Kind() == reflect.Ptr {
		return vi.IsNil()
	}
	return false
}

全部示例代码

package main

import (
	"fmt"
	"reflect"
)

type Person interface {
	Name() string
}

type ChenQiongHe struct {
}

func (t *ChenQiongHe) Name() string {
	return "雪山飞猪"
}

func main() {
	var test *ChenQiongHe
	if test == nil {
		fmt.Println("test == nil")
	} else {
		fmt.Println("test != nil")
	}
	//将空指针赋值给接口
	var person Person = test
	if IsNil(person) {
		fmt.Print("person == nil")
	} else {
		fmt.Print("person != nil")
	}
}

func IsNil(i interface{}) bool {
	vi := reflect.ValueOf(i)
	if vi.Kind() == reflect.Ptr {
		return vi.IsNil()
	}
	return false
}

运行结果

test == nil
person == nil