空指针本身不 panic,解引用 nil 才会 panic;关键是在 *p、p.Field、p.Method() 前加 p != nil 判断。Go 中仅指针、切片、map、chan、func、interface 六类可为 nil,其他类型有零值;接口 nil 不 panic,但 nil 指针调用方法若含解引用则立即 panic。

直接结论:空指针本身不 panic,解引用 nil 才会 panic;所以关键不是“消灭 nil”,而是在 *p、p.Field、p.Method() 前加 p != nil 判断。
什么时候会触发 nil pointer dereference panic
Go 中只有六类类型能为 nil:指针(*T)、切片([]T)、映射(map[K]V)、通道(chan T)、函数(func())、接口(interface{})。其他如 struct、int、string 等都有零值,不可能是 nil。
panic 仅发生在你试图「使用」一个 nil 值时:
-
*p—— 解引用 nil 指针 -
p.Field或p.Method()—— 访问 nil 指针的字段或方法 -
m["key"] = val—— 对 nil map 赋值 ch —— 向 nil chan 发送-
close(ch)—— 关闭 nil chan
注意:fmt.Println(p)、if p == nil 这些操作完全安全,不会 panic。
结构体指针返回值必须判空再用字段
常见翻车场景:函数返回 *User,但内部逻辑可能返回 nil,而调用方直接访问 user.Name。
错误写法:
user := GetUser() fmt.Println(user.Name) // 如果 GetUser() 返回 nil,这里 panic
正确做法(二选一):
- 在调用处判空:
if user != nil { fmt.Println(user.Name) } - 在函数内保证非 nil:
func GetUser() *User { return &User{} }(适合有默认语义的场景) - 组合条件判断(推荐):
if user != nil && user.ID > 0 { ... },避免短路失效
etcd Watcher、HTTP 客户端等第三方库返回的指针常为 nil
比如 etcd.Watcher 在连接断开时, 可能收到 nil 的 *etcd.Response;又如某些 HTTP 客户端封装在解析失败时返回 *Resp 为 nil。
典型错误:
r := <p>必须双检:</p>
- 检查通道是否关闭(
ok值) - 检查响应是否非 nil:
if r != nil && r.Node != nil
更稳妥写法:
r, ok := <h3>接口 nil 和指针 nil 容易混淆</h3><p>接口变量为 <code>nil</code>,不等于它持有的底层值为 <code>nil</code>。例如:</p><pre class="brush:php;toolbar:false;">var w io.Writer = os.Stdout var buf *bytes.Buffer w = buf // 此时 w 不是 nil(类型是 *bytes.Buffer,值是 nil) fmt.Println(w == nil) // false! fmt.Println(buf == nil) // true
所以不能靠 if w == nil 来判断底层是否可用;若需安全调用,应先做类型断言或设计明确的空实现(如 io.Discard)。
最易被忽略的一点:nil 接口调用方法不会 panic(只要方法签名匹配),但 nil 指针调用方法一定会 panic —— 因为接口方法调用走的是动态分发,而指针方法调用隐含一次解引用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











