错误比较

在 Go 语言中,对 error 类型的错误比较有多种形式,其中一些看似简单但隐藏着陷阱。以下是错误比较的详细分析和常见陷阱:

🎯 错误比较的 4 种形式

1. 常规错误比较 (err == target)

if err == io.EOF {
    // 处理 EOF 情况
}

​适用场景​​:比较预定义的哨兵错误(全局变量)

2. 与 nil 比较 (err == nil)

if err != nil {
    // 错误处理
}

​安全场景​​:检查函数是否返回错误

3. errors.Is()(推荐)

if errors.Is(err, io.EOF) {
    // 检查错误链中是否存在特定错误
}

​适用场景​​:处理包装错误(Wrapped Errors)

4. errors.As() 类型匹配

​适用场景​​:处理自定义错误类型


🧩 最危险的错误比较陷阱

❌ 陷阱 1:直接比较动态创建的字符串错误

​原因​​:fmt.Errorf() 每次返回新实例,内存地址不同

❌ 陷阱 2:比较被包装的错误

​正确做法​​:

❌ 陷阱 3:忽略自定义错误的不相等性

❌ 陷阱 4:错误与接口变量比较

​原因​​:io.EOF 实现了 errorio.Reader,但接口类型不同


✅ 错误处理最佳实践对比

​错误处理方式​

​使用场景​

​示例​

​推荐度​

err == nil

基础错误检查

if err != nil {...}

★★★★★

errors.Is()

匹配错误链

errors.Is(err, io.EOF)

★★★★★

errors.As()

类型断言

errors.As(err, &PathError)

★★★★★

err == target

预定义哨兵错误

err == io.EOF

★★☆☆☆

直接比较字符串

任意场景

err.Error() == "msg"

☆☆☆☆☆


🔧 自定义错误处理模式

1. 实现 Is() 方法(支持错误链)

2. 实现 Equal() 方法(用于直接比较)

3. 使用常量哨兵错误(最安全)


🚨 需要警惕的比较结果

比较表达式
结果
原因

fmt.Errorf("A") == fmt.Errorf("A")

false

不同内存实例

io.EOF == io.EOF

true

全局单实例

errors.New("A") == errors.New("A")

false

不同内存实例

error(nil) == nil

true

类型匹配

var e error = nil; e == nil

true

接口值为 nil

var e error = (*Error)(nil); e == nil

false

非空接口类型

​黄金规则​​:对于重要的错误处理,​​总是​​使用 errors.Is()errors.As() 代替 == 操作符

通过遵循这些原则,您可以避免常见的错误比较陷阱,编写出更健壮、更易维护的错误处理代码。

最后更新于