errors.is 对多层包装错误有效,因为它沿 unwrap() 链递归检查每层错误是否与目标哨兵错误相等,依赖 %w 包装、unwrap() 实现或 is() 方法,链中断(如用 %v)则匹配失败。

errors.Is 为什么对多层包装错误有效
因为 errors.Is 不只比最外层错误,而是沿着 Unwrap() 链递归检查每一层,直到找到与目标错误值相等的节点,或链结束。它依赖的是错误是否用 %w 包装 —— 只有带 %w 的 fmt.Errorf、errors.Join 或实现了 Unwrap() 的自定义错误才能被穿透。
常见错误现象:errors.Is(err, io.EOF) 返回 false,但你知道错误源头是读取结束 —— 很可能中间某层用了 %v 或字符串拼接,切断了错误链。
- 必须用包级变量(如
var ErrNotFound = errors.New("not found"))作目标,不能每次调用都errors.New("not found") -
io.EOF、os.ErrNotExist等标准哨兵错误本身支持Is()方法,所以能跨类型匹配 - 如果自定义错误类型想参与
errors.Is判定,需显式实现Is(error) bool方法,否则只靠指针比较 -
errors.Is(err, nil)永远返回false,空错误仍要用err == nil判断
errors.As 提取多层嵌套中的 *os.PathError 实例
errors.As 的作用不是“类型断言当前错误”,而是“从整条错误链中捞出第一个匹配目标类型的错误实例”。它要求你传入一个指向具体类型的指针(比如 &pathErr),然后把匹配到的值写进去。
常见错误现象:传入 pathErr(值)或 *pathErr(类型),编译失败;或传入 &pathErr 但 pathErr 是零值指针,导致静默失败。
- 第二参数必须是
&target,其中target是指针类型变量,例如var pathErr *os.PathError - 如果错误链里有多个
*os.PathError,errors.As只返回最内层那个(即最早被包装的) - 不支持接口类型直接提取,除非该接口实现了
As(interface{}) bool方法 - 若某层错误是
*os.SyscallError,而你想As成*os.PathError,会失败 —— 它们没有类型兼容关系,As不做转换,只做安全断言
包装错误时 %w 和 %v 的区别直接影响 Is/As 行为
用 %w 是建立错误链的唯一合规方式;用 %v、%s 或 fmt.Sprintf 会把错误转成字符串,彻底丢失原始错误类型和值,导致 errors.Is 和 errors.As 全部失效。
常见错误现象:日志里看到错误信息含 “permission denied”,但 errors.Is(err, os.ErrPermission) 总是 false —— 因为上游用了 fmt.Errorf("failed: %v", err)。
-
fmt.Errorf("wrap: %w", underlyingErr)✅ 支持Unwrap(),链完整 -
fmt.Errorf("wrap: %v", underlyingErr)❌ 转为字符串,链断裂 -
errors.New("wrap: " + underlyingErr.Error())❌ 同样无Unwrap(),无法穿透 - 第三方库返回的错误若没实现
Unwrap(),它在链中就是“死路”,Is/As无法越过它继续向下查
自定义错误类型如何让 Is 和 As 正常工作
如果你自己定义了一个错误结构体并封装了另一个错误,又希望它能被 errors.Is 和 errors.As 正确识别,就必须显式实现 Unwrap() 方法。否则它在错误链中就是个“黑盒”,下游无法穿透。
常见错误现象:errors.As(err, &myErr) 总是返回 false,即使 err 就是 *MyError 类型 —— 很可能漏写了 Unwrap()。
-
Unwrap()必须返回被包裹的错误,且不能返回自身或形成环,否则errors.Is会 panic - 若想让
errors.Is支持更灵活的语义匹配(比如状态码相同就算相等),需额外实现Is(error) bool方法 - 若想让
errors.As支持提取到接口类型(如Timeouter),可实现As(interface{}) bool方法 - 不要在
Unwrap()中返回nil,这会让errors.Is提前终止遍历,跳过更深层的错误
errors.Is 或 errors.As 写错了,而是上游某处悄悄用了 %v 或忘了加 Unwrap(),导致链在中途就断了。调试时优先检查错误生成处的包装方式,比反复改判断逻辑更有效。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











