err == nil 是唯一正确的错误判空方式,因Go语言契约规定成功时返回nil、失败时返回非nil error;其他方式如errors.Is(err, nil)会编译报错,转interface{}比较易失效,且需警惕Error()方法panic。

err == nil 是唯一正确的错误判空方式
Go 中 err == nil 不是“一种可选写法”,而是语言契约决定的唯一正确判断。标准库、所有主流框架、你自己写的函数,只要遵循 Go 错误约定,就一定在成功时返回 nil,失败时返回非 nil 的 error 值。
- 别用
errors.Is(err, nil)—— 编译直接报错,errors.Is只接受两个error类型参数,nil不是error - 别把
err先转成interface{}再比较:比如var i interface{} = err; if i == nil—— 即使原err是nil,只要它曾被赋给某个具体类型(如*os.PathError),i就不为nil,判断失效 - 日志或调试前别盲目打印
err:某些自定义error的Error()方法可能 panic,err != nil检查通过了,但fmt.Printf("%v", err)仍会崩溃
指针判空必须在解引用之前完成
Go 的空指针错误(nil pointer dereference)不是警告,是运行时 panic,且无法恢复(recover 对它无效)。所以关键不是“怎么 recover”,而是“绝不让解引用发生”。
- 判空永远用
p == nil,简洁安全;不要写p != nil后再进大块逻辑,容易漏掉 else 分支 - 结构体字段是指针时,即使结构体变量本身非
nil,该字段仍可能为nil,需单独检查:if u.Profile != nil { ... } - 函数参数接收指针时,若业务上不允许为空,开头就快速失败:
if p == nil { return errors.New("p must not be nil") }
接口值为 nil ≠ 接口内含的值为 nil
这是最常被误解的陷阱。接口变量 w io.Writer 是否为 nil,取决于它的动态类型和动态值是否都为空;而它内部装的指针(比如 *bytes.Buffer)是否为 nil,是另一回事。
-
var w io.Writer = nil→w == nil为 true -
var buf *bytes.Buffer; var w io.Writer = buf→w == nil为 false(因为类型已确定为*bytes.Buffer),哪怕buf本身是nil - 要检查底层指针是否为空,得先类型断言:
if b, ok := w.(*bytes.Buffer); ok && b == nil - 传参时避免把裸指针塞进
interface{},否则判空逻辑极易失控
用 reflect.IsNil 判断任意值是否“逻辑为空”要小心范围
reflect.Value.IsNil() 看似万能,但它只对指针、切片、映射、通道、函数这五类类型合法;对 int、string、结构体、数组等调用会 panic。
立即学习“go语言免费学习笔记(深入)”;
- 安全做法是先用
reflect.Value.Kind()过滤:case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func: - 对
interface{}类型,需递归展开:rv.Elem().Interface()再判,否则只判了接口容器本身 - 永远在调用
IsNil()前加rv.IsValid()防护,防止传入零值reflect.Value - 别用
reflect.DeepEqual(v, nil)替代——它行为隐晦,对未导出字段、NaN、函数等处理不可控,且掩盖了“你到底想判什么”的真实意图
空值问题的本质不是“怎么填坑”,而是清楚每种类型何时可能为 nil、nil 对它意味着什么、以及谁该负责检查。Go 不提供运行时空安全,但把控制权交给了写代码的人——这个责任没法外包给工具或包装函数。










