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