errors.is 用于穿透错误链比较语义相等性,需传入可比较的哨兵变量;errors.as 用于遍历错误链提取具体类型,目标参数必须是指针;二者均依赖 %w 包装维持链完整。

errors.Is 判断错误是否等于某个哨兵值
直接用 == 比较两个 error 值,在错误被包装后几乎总是 false——哪怕语义完全相同。这是因为 Go 的错误是值类型,fmt.Errorf("wrap: %w", io.EOF) 生成的是新对象,地址不同,err == io.EOF 必然失败。
errors.Is 的作用就是穿透整个错误链(通过反复调用 Unwrap()),检查其中任意一层是否与目标错误「相等」:要么是同一变量(如 io.EOF),要么实现了 Is(error) bool 方法并返回 true。
- 必须传入可比较的目标错误变量,例如
io.EOF、os.ErrNotExist,或你自己定义的包级变量:var ErrNotFound = errors.New("not found") - 不能传动态构造的错误:
errors.Is(err, errors.New("not found"))永远为false,因为每次errors.New都新建实例 -
errors.Is(err, nil)总是false;判断空错误仍需用err == nil - 如果底层错误(如
*os.PathError)实现了Is方法,errors.Is会优先调用它,而不是比地址
errors.As 提取包装链中最内层的具体错误类型
当你需要访问错误内部字段(比如 *os.PathError.Path)或调用其方法时,err.(*os.PathError) 类型断言会失败——它只检查最外层错误类型,而包装后的外层是 *fmt.wrapError。
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
errors.As 会自动遍历错误链,对每一层尝试类型断言为目标类型,并把第一个匹配成功的值写入你提供的指针变量中。
- 第二个参数**必须是指针**:
&pathErr,不是pathErr;传值或接口类型(如&err)会导致编译失败或未定义行为 - 目标类型需具体(如
*os.PathError或*MyAppError),不支持泛化接口(如error)作为提取目标 - 若匹配失败,函数返回
false,且目标变量保持原值(不会被置零),直接使用前务必检查返回值 - 自定义错误若想被
errors.As正确识别,需确保其类型能被安全赋值给目标指针类型(通常要求接收者为指针)
错误链断裂:%w 是唯一可靠包装方式
errors.Is 和 errors.As 都依赖错误链完整。而只有 %w 能建立可解包的包装关系;其他格式动词(%v、%s、字符串拼接)会把错误转成字符串,彻底切断链。
- ✅ 正确:
fmt.Errorf("read failed: %w", io.EOF)——Unwrap()返回io.EOF,链可继续向下 - ❌ 断链:
fmt.Errorf("read failed: %v", io.EOF)——Unwrap()返回nil,链在此终止 - ❌ 断链:
fmt.Errorf("read failed: %w, retry=%d", err, n)——%w不在末尾,Go 编译器直接报错 - ❌ 断链:
fmt.Errorf("wrap: %w", errors.New("inner"))——errors.New返回的错误不实现Unwrap(),下一层无法展开
自定义错误类型如何支持 Is/As
标准库错误(如 os.PathError)已内置 Is 和 As 支持。但如果你定义了自己的错误结构体,想让它参与链式判断,就得手动实现对应方法。
- 要支持
errors.Is:实现Is(error) bool方法,比如根据状态码或字段做语义比较,而非仅比地址 - 要支持
errors.As:实现As(interface{}) bool方法,用于更灵活的类型匹配逻辑(例如兼容多个底层类型) - 要支持被包装和解包:必须实现
Unwrap() error(单错误)或Unwrap() []error(多错误),否则出现在错误链中间就会截断 - 所有方法的接收者应为指针(
func (e *MyError) Unwrap() error),否则无法满足接口要求
%v 或漏掉 %w,整条链就失效,后续所有 Is 和 As 调用都会退化为表层判断——而这恰恰是最难排查的问题。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










