errors.Is用于安全判断错误链中是否包含特定错误,errors.As用于提取具体错误类型;二者均递归遍历错误链,避免直接比较或字符串匹配;自定义错误需实现Unwrap方法。

用 errors.Is 判断是否为特定错误
Go 1.13 引入的 errors.Is 是最常用、也最安全的方式,用于判断一个错误是否「等于」某个已知错误(包括底层包装链中的目标错误)。它不依赖指针相等,而是递归检查错误链中是否存在匹配项。
err := doSomething()
if !errors.Is(err, io.EOF) {
t.Fatal("expected io.EOF, got:", err)
}常见误用是直接用 == 比较,这在错误被 fmt.Errorf("wrap: %w", err) 包装后必然失败。只要错误可能被包装(绝大多数标准库和现代 Go 库都这么做),就必须用 errors.Is。
用 errors.As 提取具体错误类型
当需要访问错误的字段或方法(比如 *os.PathError 的 Path 字段),就得用 errors.As 将错误解包成具体类型:
var pathErr *os.PathError
if errors.As(err, &pathErr) {
if pathErr.Path == "/tmp/data.txt" {
// 处理特定路径的错误
}
}注意三点:
立即学习“go语言免费学习笔记(深入)”;
- 第二个参数必须是指向目标类型的指针(
&pathErr) -
errors.As同样遍历整个错误链,找到第一个匹配的类型即返回 true - 如果只是想确认类型存在而不需变量,仍得声明变量并传地址,无法跳过
避免用 reflect.TypeOf 或 error.Error() 做类型判断
有人会写 strings.Contains(err.Error(), "permission denied") 或 reflect.TypeOf(err).Name() == "PathError",这两种方式都不可靠:
-
Error()返回字符串可能被多层包装修改,且不同语言环境输出不同 -
reflect.TypeOf只看最外层类型,对fmt.Errorf("failed: %w", &os.PathError{})会返回errorString,而非PathError - 标准库错误类型(如
os.ErrNotExist)本身是变量,应优先用errors.Is对比,而不是靠名字或字符串猜
自定义错误类型要实现 Unwrap 方法才能被正确识别
如果你自己定义了带包装能力的错误类型,必须显式实现 Unwrap() error 方法,否则 errors.Is 和 errors.As 无法穿透到内层:
type MyError struct {
msg string
orig error
}
func (e MyError) Error() string { return e.msg }
func (e MyError) Unwrap() error { return e.orig } // 必须有这一行
漏掉 Unwrap 是测试中错误断言失效的高频原因——外部调用方根本「看不到」你包装的原始错误。










