nil pointer dereference 是 Go 里最常见的 panic,常见建议是“多判几个 nil”。这篇在本机把它拆开:报错里的 addr 其实告诉你读的是哪个字段,同样叫“空”的十种情形各发生什么,typed nil 为什么让 err != nil 为真,三个线上最常见的来源,以及怎么保住位置信息。
读 nil 指针的字段(退出码 2,用时 0.11 s):
准备读 u.Email
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x18 pc=0x102317594]
goroutine 1 [running]:
main.main()
<tmp>/basic/main.go:8 +0x54
加 recover(退出码 0,用时 0.11 s):
recover: runtime error: invalid memory address or nil pointer dereference | runtime.Error: true三部分信息,是 panic 可 recover,抓到的是 runtime.Error。
访问 t.A(偏移 0):[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x100901dd0] 访问 t.B(偏移 8):[signal SIGSEGV: segmentation violation code=0x2 addr=0x8 pc=0x1024bddac] 访问 t.C(偏移 16):[signal SIGSEGV: segmentation violation code=0x2 addr=0x10 pc=0x102861d88] 访问 b.X(偏移 1 MiB):[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x104b5de18]
addr 是字段偏移,可以反推是哪个字段;偏移很大时变回 0x0。
写 nil map: m["a"]=1:退出码 2,首行:panic: assignment to entry in nil map 读 nil map: m["a"]:退出码 0,首行:读到 0 0 nil slice append / len / range:退出码 0,首行:append 后 [1] 1 下标访问 nil slice: s[0]:退出码 2,首行:panic: runtime error: index out of range [0] with length 0 调用 nil 函数变量:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference 调用 nil interface 的方法:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference 指针接收者方法里不碰字段(nil 接收者):退出码 0,首行:返回 nil 接收者,没出错 值接收者方法用 nil 指针调用:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference 指针接收者方法里读字段:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference 读 nil 指针的 map 字段: t.M["a"]:退出码 2,首行:panic: runtime error: invalid memory address or nil pointer dereference
读 nil map 不错,写 nil map 另一句 panic;nil 接收者不碰字段也不错。
typed nil(退出码 0,用时 0.32 s):
bad(): err != nil 为 true,类型 *main.MyErr,值 true
good(): err != nil 为 false,类型 <nil>
进入 if 分支,准备 err.Error()
recover: runtime error: invalid memory address or nil pointer dereference返回有类型的 nil 指针使 err != nil 为真,没有错就 return nil。
http.Get 失败后直接 defer resp.Body.Close()(退出码 2,用时 0.37 s):
err = true
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x40 pc=0x100d6a1bc]
goroutine 1 [running]:
main.main()
map[string]*User 取不存在的 key 再 .Name(退出码 2,用时 0.35 s):
alice
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x104fdf694]
goroutine 1 [running]:
main.main()
json 里缺了可选的嵌套对象(退出码 2,用时 0.33 s):
Retry == nil: true
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x100348230]
goroutine 1 [running]:
main.main()忘判 err、map 取不到、JSON 缺可选对象。
recover + debug.Stack(退出码 0,用时 0.31 s):
recover 只有: runtime error: invalid memory address or nil pointer dereference
debug.Stack 里: <tmp>/stack/main.go:12 +0x5c
debug.Stack 里: main.level3(...)
debug.Stack 里: <tmp>/stack/main.go:5
debug.Stack 里: main.level2(...)
debug.Stack 里: <tmp>/stack/main.go:6
debug.Stack 里: main.level1(...)
debug.Stack 里: <tmp>/stack/main.go:7
…(共 9 行)recover 里用 debug.Stack() 保留栈,只打印值没有位置。