errors.is 用于沿错误链检查是否等于目标哨兵错误,需用 errors.new 定义包级变量并配合 %w 包装;errors.as 用于提取底层错误类型,要求传入指针且错误链中含 %w。

errors.Is 判断错误是否等于某个目标错误
errors.Is 用来做「错误相等性判断」,核心是沿着错误链(wrapped error)向上检查,看是否存在某一层与目标错误 == 相等(即同一地址或实现了 Is(error) bool 方法并返回 true)。
常见错误现象:用 == 直接比较两个 error 值,结果总是 false —— 因为大多数错误是新构造的实例,地址不同。
实操建议:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 只对明确用
errors.New或fmt.Errorf(未包装)创建的哨兵错误(sentinel error)用errors.Is判断 - 不要对动态生成的错误字符串做
errors.Is(err, errors.New("xxx")),这永远不成立——每次errors.New都新建一个对象 - 推荐定义包级变量作为哨兵:
var ErrNotFound = errors.New("not found"),再用errors.Is(err, ErrNotFound) - 若底层错误实现了
Is方法(比如os.PathError),errors.Is会调用它,而不是比地址
errors.As 提取底层具体错误类型
errors.As 解决的是「错误类型断言」问题:当错误被多层包装(例如 fmt.Errorf("wrap: %w", os.ErrPermission)),你无法用普通 err.(*os.PathError) 断言成功,必须逐层展开。
errors.As 自动遍历错误链,找到第一个匹配目标类型的错误值,并把指针赋给传入的变量。
实操建议:
- 第二个参数必须是指向接口或具体类型的指针,例如
&pathErr,不是pathErr - 如果错误链中没有匹配类型,返回 false,且目标变量保持零值;不会 panic
- 常见误用:传入非指针,如
errors.As(err, pathErr)→ 编译失败(类型不匹配) - 支持接口类型提取,例如
var target interface{ Timeout() bool },再传&target
包装错误时记得用 %w,否则 errors.Is/As 失效
只有用 %w 动词包装的错误才会被 errors.Is 和 errors.As 识别为「可展开的 wrapped error」。用 %s、fmt.Sprintf 或字符串拼接都会切断错误链。
错误示例:
// ❌ 切断错误链
err := fmt.Errorf("failed to read: %s", io.EOF)
// ✅ 保留错误链
err := fmt.Errorf("failed to read: %w", io.EOF)
实操建议:
- 所有需要下游做错误判断或提取的场景,一律用
%w - 日志打印时可用
%v或%+v(需第三方库如github.com/pkg/errors)查看完整链,但标准库fmt.Printf("%+v", err)对原生fmt.Errorf效果有限 - 自定义错误类型若想支持
%w,需实现Unwrap() error方法
性能与兼容性注意点
errors.Is 和 errors.As 在错误链很深时会有轻微开销(O(n) 遍历),但日常使用完全可忽略。真正要注意的是 Go 版本和行为差异。
实操建议:
- Go 1.13 引入
errors.Is/As,低于此版本需升级或用golang.org/x/xerrors(已归档,不推荐新项目) -
errors.As在 Go 1.20+ 对泛型支持更稳,但提取时仍要求目标类型在编译期确定,不能是interface{}变量 - 不要在热循环里反复调用
errors.As提取同一类错误——提前提取一次,缓存结果更高效 - 若错误来自 Cgo 或 syscall,某些底层错误(如
syscall.Errno)可能不满足errors.Is的预期行为,建议用errors.As提取后直接比errno值
最常被忽略的一点:errors.Is 不是字符串匹配,也不是模糊查找;它只认地址相等或显式声明的 Is 逻辑。写错哨兵变量定义位置、漏传 %w、或者对非包装错误强行用 As,都会让整套机制静默失效。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










