interface{} == nil 仅当 type 和 data 均为零值时才成立;赋值 (*string)(nil) 后 type 非空、data 为空,故不等于 nil;正确判空需类型断言或谨慎使用 reflect.Value.IsNil()。

直接用 == nil 只能判断接口值本身是否为空,无法检测它内部装的是否是带类型的 nil 指针、nil 切片等——这是最常踩的坑。
为什么 interface{} == nil 经常失效
Go 的 interface{} 在内存中是两字宽结构:type + data。只有二者都为零值时,== nil 才返回 true。但当你把 (*string)(nil) 赋给 interface{},type 是 *string(非空),data 是 nil(空),整个接口就不等于 nil。
常见错误现象:
-
var s *string = nil; var i interface{} = s; fmt.Println(i == nil)→ 输出false - 后续做
s, ok := i.(*string); if ok && s != nil才能真正拿到“有值”的指针 - 若直接调
s.String()或解引用,可能 panic
用 reflect.ValueOf(i).IsNil() 前必须过三关
reflect.Value.IsNil() 不是万能钥匙,乱用必 panic。它只对六种类型有效:Ptr、Slice、Map、Chan、Func、UnsafePointer;对 int、string、struct 等调用会直接崩溃。
立即学习“go语言免费学习笔记(深入)”;
安全使用流程(缺一不可):
- 先检查
i == nil→ 若为true,直接返回true - 再调
v := reflect.ValueOf(i),并确认v.IsValid()→ 否则v.Kind()无意义 - 再用
v.Kind()判断是否属于上述六种之一;若为Interface,需额外v.Elem().IsValid()后再查.IsNil()
示例片段:
if i == nil {
return true
}
v := reflect.ValueOf(i)
if !v.IsValid() {
return false
}
switch v.Kind() {
case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func:
return v.IsNil()
case reflect.Interface:
if !v.Elem().IsValid() {
return true // 底层是 nil 接口
}
return v.Elem().IsNil()
default:
return false // int/string/struct 等值类型不可能为 nil
}
更轻量:按已知类型做断言 + 显式判空
如果你清楚接口大概率装的是哪几种类型(比如 HTTP handler 中的 error、或自定义的 *User、map[string]interface{}),类型断言比反射快且清晰,也更容易 debug。
- 对
error:标准库约定nilerror 就是接口值为nil,所以err != nil安全可用 - 对指针类型:
if p, ok := i.(*MyStruct); ok && p == nil - 对切片或 map:
if s, ok := i.([]byte); ok && s == nil或len(s) == 0(注意:nilslice 和空 slice 行为不同) - 对 channel / func:
if ch, ok := i.(chan int); ok && ch == nil
真正难处理的,是泛型函数或通用序列化逻辑里那种“完全不知道里面是什么”的 interface{} 参数——这时候反射不是银弹,而是唯一能兜底的手段;但高频路径(如日志中间件每请求一次)要警惕性能损耗,10–50 倍慢是实测数据。别为了“看起来统一”牺牲可维护性:该分 case 就分 case,该加文档就写清“本函数接受 *T,传 nil 指针是合法行为”。


















