Go中error接口为nil的条件是动态类型和动态值同时为nil;errors.New等返回的非空指针使接口不为nil;自定义error应显式return nil而非&MyError{}。

Go 中 error 接口为 nil 的真实判断条件是什么
Go 里 error 是接口类型,所以 nil 判断不是看底层值是否为空,而是看整个接口的动态类型和动态值是否都为 nil。常见错误是只返回了 &MyError{} 这种非空指针,但接口变量本身不为 nil,导致 err == nil 判断失败。
- 只有当接口的动态类型(type)和动态值(value)同时为
nil,err == nil才成立 - 如果函数返回
return &MyError{},即使结构体字段全零,err也不为nil - 正确做法:显式返回
nil,而不是返回一个非空指针或空结构体实例 - 反例:
func bad() error { return &errors.New("x").(error) }—— 返回的是具体 *errorString,接口不为nil
为什么 errors.New("x") == nil 永远是 false
errors.New 返回的是 *errors.errorString 类型的指针,它被赋给 error 接口后,接口的动态类型是 *errors.errorString,动态值是非空地址,因此整个接口不为 nil。这不是 bug,是 Go 接口设计的必然结果。
-
errors.New("x")和fmt.Errorf("x")都返回非nil的error接口值 - 哪怕你写
var err error = errors.New(""),err == nil仍是false - 想让它为
nil?只能直接赋值err = nil或函数明确return nil - 别试图用
reflect.ValueOf(err).IsNil()判断——对接口类型会 panic
自定义 error 类型时如何避免 nil 判断失效
定义自己的 error 类型时,最容易踩的坑是让方法返回一个非空指针,却误以为“空结构体 = nil error”。只要类型实现了 error 接口,它的任何非 nil 实例都会让接口变量非 nil。
- 不要这样写:
return &MyError{}(除非你确定调用方能处理非nilerror) - 推荐写法:
if cond { return nil };出错时才return &MyError{...} - 如果必须封装,提供一个
IsNil() bool方法,内部检查关键字段(如e.msg == "" && e.code == 0),但注意这和语言级== nil无关 - 慎用嵌入:
type MyError struct { error }—— 如果嵌入的error字段为nil,整个结构体仍非nil,且MyError{}本身不实现error,除非你补上Error()方法
调试时怎么快速确认 error 接口到底是不是 nil
光看 fmt.Println(err) 或 log.Printf("%v", err) 不够,因为 nil 接口打印出来也是 <nil></nil>,而某些非 nil error 打印也像空字符串。得看底层。
立即学习“go语言免费学习笔记(深入)”;
- 最直白方式:
fmt.Printf("err == nil? %t\n", err == nil) - 查类型+值:
fmt.Printf("type: %T, value: %+v\n", err, err)—— 如果输出type: *errors.errorString, value: &{s:"xxx"},说明不为nil - 在 debugger(如 Delve)中,观察
err._type和err.data是否都为0x0 - 别依赖
err.Error() == ""判断:有些 error 实现返回空字符串,但它依然非nil
接口的 nil 是类型系统层面的双空判定,不是业务逻辑里的“有没有内容”。写函数时,返回 nil 就老老实实写 return nil;接收到 error,就只用 == nil 判断,别绕路、别脑补、别加额外字段检查——除非你明确知道自己在做语义扩展。










