Go中仅指针、切片、映射、通道、函数、接口六类类型支持==nil;其他类型用nil会编译报错,接口判空需注意动态类型与值均为空才为true。

Go里不能对非引用类型用 nil,也不能靠 == nil 安全判断所有接口值——这是最常踩的两个坑,直接导致 panic 或逻辑错漏。
哪些类型能和 nil 比较
只有六类类型支持 == nil:指针(*T)、切片([]T)、映射(map[K]V)、通道(chan T)、函数(func())、接口(interface{})。其他一律编译报错。
-
string、int、struct{}等值类型零值不是nil,写var s string = nil会报cannot use nil as type string -
[]int(nil)和map[string]int(nil)合法,但[]int(nil) == []string(nil)编译失败——类型不同不可比 - 接口能比
nil,但语义特殊:仅当动态类型和动态值都为空时,i == nil才为true
interface{} 的 == nil 为什么经常失效
因为接口是两字宽结构体(类型指针 + 数据指针),只要类型信息存在,哪怕数据是 nil 指针,整个接口也不等于 nil。
-
var p *bytes.Buffer = nil; var w io.Writer = p→ 此时w == nil是false,但w.Write(...)会 panic -
fmt.Printf("%#v", w)输出(*bytes.Buffer)(nil),容易误以为接口是nil - 正确做法是先确认接口非空,再用反射或类型断言检查底层值:
reflect.ValueOf(w).IsNil()或if bw, ok := w.(*bytes.Buffer); ok && bw == nil
切片判空:区分 nil 和空切片
s == nil 和 len(s) == 0 不等价。前者表示未初始化,后者只是长度为 0 —— 行为差异直接影响 append、range 和 JSON 序列化。
立即学习“go语言免费学习笔记(深入)”;
-
var s []int:s == nil为true,append(s, 1)返回新切片,但s[0]panic -
s := []int{}:s == nil为false,append(s, 1)可原地扩容,range s安全不迭代 - 传给接口后(如
func f(i interface{})),s无论nil还是空,i == nil都为false—— 接口已装箱,类型字段非空
高频场景下的安全判空模式
别依赖单一 == nil,按上下文组合判断才可靠。
- 接收
error参数:直接if err != nil即可,标准库约定error接口为nil即无错误 - 处理用户传入的
interface{}:先if i != nil,再switch v := i.(type)分支处理,对指针/映射/切片分别用v == nil或len(v) == 0 - 结构体嵌套指针字段:逐层判空,例如
if u != nil && u.Profile != nil && u.Profile.Address != nil,别合并成一行 - 性能敏感路径避免
reflect.ValueOf(i).IsNil(),它比直接比较慢 10–50 倍,且对非指针/非引用类型调用会 panic
真正麻烦的从来不是 nil 本身,而是它在接口中“隐身”后的双重身份:接口值不为 nil,但底层值可能已经失效。每次把指针、切片塞进 interface{} 前,都要想清楚——你到底想检查接口容器,还是里面那个值。


















