nil
约 693 字大约 2 分钟
2024-05-19
nil 是 Go 语言中预先声明的标识符(predeclared identifier),用作 pointer、channel、func、interface、map、slice 这六种类型的零值。
// 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 typenil 与 pointer、channel、func、map、slice比较
对于指针类型(pointer)变量,只有其未指向任何对象时候,才能等于 nil:
func main() {
var p *int
fmt.Println(p == nil) // true
a := 100
p = &a
fmt.Println(p == nil) // false
}对于channel、func、map、slice,只有 var t T 或者手动赋值为nil时,才等于nil
func main() {
// slice
var s []int // nil slice s.array == nil
fmt.Println(s == nil) // true
s = make([]int, 0) // empty slice; s.array == &zerobase
fmt.Println(s == nil) // false
// map
var m map[int]int
fmt.Println(m == nil) // true
m = make(map[int]int)
fmt.Println(m == nil) // false
// channel
var ch chan int
fmt.Println(ch == nil) // true
ch = make(chan int)
fmt.Println(ch == nil) // false
// 函数
var fn func()
fmt.Println(fn == nil) // true
fn = func() {}
fmt.Println(fn == nil) // false
}zerobase是 Go runtime 提供的全局固定地址,用作所有零字节分配的起始基址,保证即使长度为 0,底层指针仍合法且非 nil
nil 与 接口比较
接口类型变量根据是否包含方法,分为 eface(空接口) 和 iface(非空接口) 两种形式,但它们在语义上都可以抽象为包含两个基础属性:
- Type:接口中保存的动态类型信息
- 在 eface 中对应
*_type - 在 iface 中由
*itab间接表示
- 在 eface 中对应
- Value:接口中保存的动态值,即底层数据指针
data unsafe.Pointer
接口是否为 nil,取决于 Type 和 Value 是否同时为 nil,而不是仅仅取决于 Value
func main() {
var p *int = nil
var i interface{} // (_type=nil, data=nil)
fmt.Println(i == nil) // true
var i2 interface{} = p // (_type=*int, data=nil)
fmt.Println(i2 == nil) // false _type不为nil
fmt.Println(i2 == i) // false _type不一致
fmt.Println(p == i) // false p隐式转换为interface{}(_type=*int, data=nil),与i的(_type=nil, data=nil)不同
// (*interface{})(nil) 是一个具体类型(*interface{})的nil指针,赋值给interface{}时会填入_type
var i3 interface{} = (*interface{})(nil) // (_type=*interface{}, data=nil)
fmt.Println(i3 == nil) // false
// (interface{})(nil) 只是将无类型nil转为interface{}自身的零值,等价于 var i4 interface{} = nil
var i4 interface{} = (interface{})(nil) // (_type=nil, data=nil)
fmt.Println(i4 == nil) // true
}nil 值的操作行为
并非所有 nil 值都能安全操作,以下是常见的坑点:
| 类型 | 操作 | 行为 |
|---|---|---|
| nil map | 读取 | 返回零值,不会 panic |
| nil map | 写入 | panic: assignment to entry in nil map |
| nil channel | 发送/接收 | 永久阻塞 |
| nil channel | close | panic: close of nil channel |
| nil slice | append | 正常工作,返回新 slice |
| nil slice | len/cap | 返回 0 |
| nil pointer | 解引用 | panic: invalid memory address or nil pointer dereference |
| nil func | 调用 | panic: invalid memory address or nil pointer dereference |
