go语言:nil说明

介绍

nil 是 Go 的预定义标识符,不是关键字,代表「零值」,只能赋值给特定类型的变量;nil 本身没有类型,会随上下文推导类型。

类比

类似Java的 null

1. 哪些类型可以为 nil

只有下面这 6 种引用/指针类型,它们的零值才是 nil:

  1. 指针 *T
  2. 切片 []T
  3. map map[K]V
  4. 通道 chan T
  5. 函数 func()
  6. 接口 interface{}

✅ 其他类型零值不是 nil:

  • int → 0
  • string → “”
  • bool → false
  • 结构体 → 所有字段置零
var a *int        // nil
var b []int       // nil
var c map[string]int // nil
var d chan int    // nil
var e func()      // nil
var f interface{} // nil

var g int         // 0,不是 nil
var h string      // "",不是 nil

2. nil 没有类型,动态推导

nil 是无类型常量,编译器会根据接收变量确定 nil 的类型。

var p *int = nil
var s []int = nil
// p 和 s 的 nil 不是同一个东西,类型不同

坑

不能直接写 nil == nil,编译器报错,因为缺少类型信息。

// invalid operation: nil == nil (untyped nil == untyped nil)
if nil == nil {
}

3. 接口的 nil:最容易踩坑!

接口变量由两部分组成:动态类型 + 动态值

接口 == (type, value)

只有 type 和 value 同时为 nil,接口变量才等于 nil。
如果 type 不为 nil,value 是 nil,接口不等于 nil。

示例:

type MyErr struct{}
func (e *MyErr) Error() string { return "err" }

func test() error {
    var e *MyErr = nil // value是nil,类型是*MyErr
    return e           // 返回接口:( *MyErr, nil )
}

func main() {
    err := test()
    fmt.Println(err == nil) // false!!
}

解释:
接口变量 err 的动态类型是 *MyErr(不为nil),动态值是 nil,所以 err != nil。

✅ 修复:返回真正 nil 接口

func test() error {
    var e *MyErr = nil
    if e == nil {
        return nil // 返回 (nil, nil) 的接口
    }
    return e
}

4. 各类型 nil 的行为差异

指针 nil

  • var p *int = nil
  • 不能解引用 *p → panic
  • 可以取地址 &p

切片 nil

var s []int = nil
fmt.Println(len(s)) // 0
fmt.Println(cap(s)) // 0

✅ nil 切片可以调用 len、cap,可以 append
append nil slice 会自动分配底层数组,不会panic。

var s []int = nil
s = append(s, 1) // 正常,s 变成 [1]

nil切片 和 空切片 []int{}:len、cap都是0,但底层数组指针不一样。

map nil

var m map[string]int = nil
fmt.Println(m["a"]) // 可以读,返回值类型零值
m["a"] = 1          // panic: assignment to entry in nil map

✅ 读 nil map 没问题;❌ 写 nil map 直接 panic

channel nil

var ch chan int = nil
<-ch // 阻塞,永远等待
ch <- 1 // 阻塞,永远等待
close(ch) // panic: close of nil channel

nil channel 收发都会永久阻塞,不能 close。

func nil

var f func() = nil
f() // panic: call of nil function

nil函数变量调用会panic。

5. nil 比较规则

  1. 同类型的 nil 值可以比较:var p1,p2 *int = nil; p1 == p2 → true
  2. 不同类型 nil 不能直接比较:var p *int; var s []int; p == s 编译报错
  3. 接口比较:按上面的(type,value)规则