go中空指针解引用必然panic,必须在每次解引用前显式判空;函数返回*t,error时须先检查err再使用指针;结构体指针字段、接口断言后指针、嵌套指针等均需逐层判nil。

Go里空指针 panic 不是“可能出错”,而是“只要解引用就必然崩溃”——panic: runtime error: invalid memory address or nil pointer dereference 这条错误信息出现时,代码已经越过了安全边界。关键不是事后 recover,而是在每次解引用前主动拦截。
函数返回指针时必须先检查 error 再用指针
Go 标准库中大量函数(如 http.Get、os.Open、json.Unmarshal)返回 *T, error,但文档不保证指针非 nil;出错时指针恒为 nil,此时直接访问字段或调用方法必崩。
-
httpRequest, err := http.Get(url)后,必须写if err != nil { return err },再用httpRequest -
f, err := os.Open("config.json")后,不能跳过err直接defer f.Close(),否则f为nil时f.Close()panic -
json.Unmarshal(data, &user)若user.Name是*string字段,且 JSON 中没传name,该字段仍为nil,后续*user.Name就会崩
结构体指针字段访问前必须显式判空
声明 type User struct { Name *string } 后,用 User{} 初始化,Name 就是 nil;这不是 bug,是 Go 的零值语义。但一旦业务逻辑里写 fmt.Println(*u.Name),就等于主动触发 panic。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 所有指针字段在读取前加
if u.Name != nil,哪怕只用一次 - 若字段语义上“必须有值”,就别用
*string,改用string;允许为空才用指针,且文档/注释明确说明 - 构造函数里主动初始化:比如
NewUser(name string) *User { return &User{ Name: &name } },避免裸字面量初始化
接口变量断言后还要再判指针是否为 nil
interface{} 类型本身可为 nil,但更危险的是:它底层值是 *User,而该指针本身又是 nil。此时 u := data.(*User) 不 panic,但 u.Name 会 panic。
- 错误写法:
u := data.(*User); fmt.Println(u.Name)——data是nil或底层指针是nil都会崩 - 正确写法:
if u, ok := data.(*User); ok && u != nil { fmt.Println(u.Name) } - 从
map[string]interface{}取值后同理:先if v, ok := m["user"]; ok,再if u, ok := v.(*User); ok && u != nil
map 和 slice 的 nil 与空要分清,但指针字段永远不隐式安全
nil map 和 nil slice 行为不同:nil map 写入就 panic,nil slice 读长度不 panic,但它们都不影响指针字段的安全性判断。真正容易被忽略的是:结构体里嵌了 *map[string]string 或 *[]int ——这种双重指针字段,解引用前得连判两次 != nil。
-
if u.ConfigMap != nil { for k := range *u.ConfigMap { ... } }—— 先判外层指针,再解引用取 map -
if u.Ints != nil && len(*u.Ints) > 0——*u.Ints是 slice,len安全,但前提是u.Ints本身不为nil - 没有“默认安全”的指针:哪怕你刚 new 出来,只要中间经过序列化/反序列化、channel 传递、接口包装,就可能变
nil
最常被绕过的检查点是:从 JSON 或 gRPC 解析后的结构体,其指针字段的 nil 状态完全取决于输入数据是否存在对应 key;没人手动赋值,它就永远是零值 nil。这时候靠注释或文档说“这个字段非空”毫无意义,运行时该崩还是崩。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










