Golang nil使用避坑
Golang 中的 nil
是一个很特殊的值,它不同于其他编程语言中的 null
、None
或 nil
。在 Golang 中,nil
可以用于表示接口、函数、通道、映射、切片等的零值。虽然 nil
在某些情况下非常有用,但如果不正确地使用它,也会导致一些很难发现的错误。
各类型变量的默认值
- 布尔类型的零值(初始值)为
false
- 数值类型的零值为
0
- 字符串类型的零值为空字符串
""
- 其他的均为
nil
nil
的数据类型
- 单独的一个
nil
值本身没有类型,只有通过上下文,判断其赋值对象,才能判断其类型 - 不同类型的
nil
值无法进行比较 - 不同类型的
nil
值,内存大小也不一样 - nil其实包含两个值,分别是type和data
func main() {
var p *struct{} = nil
fmt.Println(unsafe.Sizeof(p)) // 8
var s []int = nil
fmt.Println(unsafe.Sizeof(s)) // 24
var m map[int]bool = nil
fmt.Println(unsafe.Sizeof(m)) // 8
var c chan string = nil
fmt.Println(unsafe.Sizeof(c)) // 8
var f func() = nil
fmt.Println(unsafe.Sizeof(f)) // 8
var i interface{} = nil
fmt.Println(unsafe.Sizeof(i)) // 16
}
关于nil的一些踩坑记录
将 nil
赋值给非接口类型
在 Golang 中,你不能将 nil
直接赋值给非接口类型,例如:
var x string = nil // 同理 int/float/bool等类型也不可以
这将导致编译错误:cannot use nil as value in assignment
。要避免这个问题,可以将 nil
赋值给非接口类型之前,先将其定义出来再赋值,例如:
var x string = ""
此类情况一般IDE可以提前识别到,在无IDE情况下需要注意。
切片判空慎用nil
func F() {
// 定义变量
var s []string
fmt.Printf("1:nil=%t\n", s == nil) // true
// 组合字面量方式
s = []string{}
fmt.Printf("1:nil=%t\n", s == nil) // false
// make方式
s = make([]string, 0)
fmt.Printf("1:nil=%t\n", s == nil) // false
}
func A()[]string{
...
return nil
}
func B(){
c := A()
// 判断c是否是空数组
if c == nil {} // 不推荐
if len(c) == 0 {} // 推荐
}
nil
与any
的使用
nil
与any
比较类似,本身并不代表任何类型,仅能通过上下文调用才可以判断类型
func F(){
var x *string
var y *string
BothNil := func(a any, b any) bool {
return a == nil && b == nil
}
BothNilInterface := func(a interface{}, b interface{}) bool {
return a == nil && b == nil
}
fmt.Println(x == nil && y == nil) // true
fmt.Println(BothNil(nil, nil)) // true
fmt.Println(BothNil(x, y)) // false
fmt.Println(BothNilInterface(nil, nil)) // true
fmt.Println(BothNilInterface(x, y)) // false
}
因为
any
/interface{}
数据,其定义中不仅包含其所代表的值,同样还有其代表值的类型。直接使用any
/interface{}
做nil判断,不仅需要判断date是否为nil,还需要判断其类型是否为空(类型只要被赋值interface{},一般就不会为空)
// goloang源码runtime.eface定义
type eface struct { // 16 字节
_type *_type
data unsafe.Pointer
}
是否有其他方法可以规避,有,但还有别的坑
func F(){
var x *string
var y *string
BothNil := func(a any, b any) bool {
return reflect.ValueOf(a).IsNil() && reflect.ValueOf(b).IsNil()
}
fmt.Println(BothNil(x, y)) // true
var xx int
var yy int
fmt.Println(BothNil(xx, yy)) // panic
}
// 或者用一个同类型的nil做比较
BothEq := func(a any, b any) bool {
return a == b
}