应优先用errors.As判断错误链中是否存在某类型,因其能逐层Unwrap;errors.Is用于检查特定错误值(如os.ErrNotExist);自定义错误只需实现Unwrap()方法即可支持二者。

如何判断错误是否是某个具体类型(比如 *os.PathError)
直接用类型断言最常见,但要注意:只有当错误值底层是目标类型时才成功。如果错误被包装过(比如用 fmt.Errorf("wrap: %w", err)),普通类型断言会失败。
示例场景:想确认错误是否源于路径不存在,需检查是否为 *os.PathError:
if pathErr, ok := err.(*os.PathError); ok {
if pathErr.Err == os.ErrNotExist {
// 处理文件不存在
}
}
⚠️ 容易踩的坑:
- 误对
error接口做多级指针断言(如**os.PathError),实际值通常是一级指针 - 在错误链中只检查最外层,忽略内层包装导致漏判
- 对非指针类型错误(如
os.ErrNotExist本身)做*os.PathError断言必然失败
errors.As 为什么比类型断言更可靠
errors.As 会沿着错误链逐层调用 Unwrap(),直到找到匹配的底层错误类型。它不依赖“最外层是什么”,而是解决“这个错误链里有没有我要的类型”。
适用条件:
- 错误由
%w包装(支持标准错误链) - 目标类型实现了
error接口且可寻址(通常传指针) - 你关心的是“是否存在该类型”,而非“是否正好是这一层”
正确用法:
var pathErr *os.PathError
if errors.As(err, &pathErr) {
if pathErr.Err == os.ErrNotExist {
// 安全,pathErr 已赋值
}
}
❌ 错误写法:errors.As(err, pathErr)(没取地址)、errors.As(err, &err)(类型不匹配)
什么时候该用 errors.Is 而不是 errors.As
errors.Is 判断的是“错误链中是否存在某个**具体错误值**”,比如 os.ErrNotExist、sql.ErrNoRows;而 errors.As 判断的是“是否存在某个**类型实例**”。两者目的不同,不可互换。
典型选择逻辑:
- 检查是否是“文件不存在”这个特定语义 → 用
errors.Is(err, os.ErrNotExist) - 需要访问
*os.PathError的Op或Path字段 → 用errors.As(err, &pathErr) - 既要看语义又要取字段?先
Is快速判断,再As提取结构
性能提示:两者都短路遍历错误链,但 Is 比较值,As 需分配并类型匹配,后者开销略高。
自定义错误类型如何支持 errors.As 和 errors.Is
只需让自定义错误实现 Unwrap() error 方法(返回包装的下一层错误),就能被标准库函数识别。不需要额外接口或继承。
示例:
type MyError struct {
Msg string
Code int
Err error // 包装的底层错误
}
func (e *MyError) Error() string { return e.Msg }
func (e *MyError) Unwrap() error { return e.Err }
这样之后,errors.As(wrappedErr, &myErr) 就能穿透 MyError 找到内部的 *os.PathError。
⚠️ 注意:如果 Unwrap() 返回 nil,错误链终止;如果返回自身(如 return e),会导致无限循环 panic。










