应优先用errors.As判断错误链中是否存在某类型,因其能逐层Unwrap;errors.Is用于检查特定错误值(如os.ErrNotExist);自定义错误只需实现Unwrap()方法即可支持二者。
直接用类型断言最常见,但要注意:只有当错误值底层是目标类型时才成功。如果错误被包装过(比如用 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 会沿着错误链逐层调用 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 判断的是“错误链中是否存在某个**具体错误值**”,比如 os.ErrNotExist、sql.ErrNoRows;而 errors.As 判断的是“是否存在某个**类型实例**”。两者目的不同,不可互换。
典型选择逻辑:
errors.Is(err, os.ErrNotExist)
*os.PathError 的 Op 或 Path 字段 → 用 errors.As(err, &pathErr)
Is 快速判断,再 As 提取结构性能提示:两者都短路遍历错误链,但 Is 比较值,As 需分配并类型匹配,后者开销略高。
只需让自定义错误实现 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。