go中直接比较nil是唯一安全标准做法:指针、接口、切片、map、channel、func可与nil比较;struct值等基础类型不可;接口判空需注意类型信息存在时即使底层指针为nil也不等于nil。

Go 中直接比较 nil 是唯一安全且标准的做法
Go 的指针类型(包括自定义结构体指针、切片、map、channel、func、interface)和 nil 比较是语言原生支持的,不需要反射或额外函数。只要变量声明为指针类型(如 *T),就能直接用 == nil 或 != nil 判断。
常见错误是试图对非指针类型(比如普通 struct 值、int)做 == nil,编译器会直接报错:invalid operation: cannot compare ... (missing type);或者误以为 reflect.Value.IsNil() 能用于所有情况——它只接受 reflect.Value 且要求 Kind 是指针、map、slice 等,用错类型 panic。
- 只有指针类型(
*T)、接口(interface{})、切片([]T)、map(map[K]V)、channel(chan T)、func(func())能与nil比较 - struct 值、int、string、bool 等基础类型不能和
nil比较,编译失败 - 接口变量为
nil,当且仅当其动态类型和动态值都为nil;若接口里存了 *T 但 *T 本身是nil,接口不为nil
接口类型判断 nil 容易踩坑:别只看底层指针
接口不是指针,它由两部分组成:类型信息(type)和值信息(data)。即使你把一个 *T 的 nil 赋给接口,接口本身不为 nil,因为类型信息已存在。这是最常被忽略的逻辑漏洞。
例如:var p *bytes.Buffer = nil; var w io.Writer = p,此时 w == nil 是 false,但 w.Write([]byte{}) 会 panic,因为底层指针是 nil。
立即学习“go语言免费学习笔记(深入)”;
- 检查接口是否为
nil:直接if w == nil - 检查接口底层具体值是否为空:需先类型断言,再判空,如
if bw, ok := w.(*bytes.Buffer); ok && bw == nil - 更稳妥的做法是在接收接口参数时,文档或注释明确约定是否允许底层为
nil,并在函数内部做二次校验
结构体指针字段判空别漏掉嵌套层级
结构体中嵌套指针字段(如 user.Profile *Profile)时,判空必须逐层展开。直接 user.Profile.Name 可能 panic,而 user.Profile != nil && user.Profile.Name != "" 才安全。
容易犯的错是写成 if user.Profile != nil { ... user.Profile.Name ... } 却忘了 Profile 内部还有指针字段(比如 Profile.Address *Address),结果在下一层又 panic。
- 多层嵌套建议拆开判断,避免一行写太长:先
if user.Profile != nil,再if user.Profile.Address != nil - 不要依赖 defer recover 捕获这类 panic——它是错误处理,不是空值控制流
- 如果嵌套很深且高频访问,可封装方法如
user.ProfileName(),内部统一处理空值返回默认值或 error
用 errors.Is(err, nil) 判错是错的
err 是接口类型,判空就用 err == nil。写成 errors.Is(err, nil) 不仅多余,而且行为异常:errors.Is() 第二个参数必须是非 nil 的 error,传 nil 会导致 panic(Go 1.20+ 会 panic,早期版本可能返回 false)。
类似地,errors.As() 和 errors.Unwrap() 也不适用于判空场景。
- 正确:
if err != nil { ... } - 错误:
if errors.Is(err, nil) { ... }→ 编译不报错但运行 panic - 注意
fmt.Errorf("...: %w", nil)生成的 error 不为nil,所以err == nil仍是可靠依据
== nil,而是想清楚「这个变量在什么路径下可能没被初始化」「它的上游是否保证了非空」「接口背后藏的是不是裸指针」——这些没法靠语法检查,得靠上下文和测试覆盖。










