Go errors.Is 为什么不会反向调用目标值的 Is
errors.Is(err, target) 不会反向调用 target.Is(err),因为它的契约是搜索第一个参数 err 的错误树。对树中的每个当前节点,标准库先判断它是否等于 target,再看这个当前节点是否实现 Is(error) bool 并调用 current.Is(target)。target 只是匹配目标,不是另一个需要遍历的错误树。
官方文档:https://pkg.go.dev/errors#Is
- 匹配方向固定为
err到target,不是双向协议。 - 自定义
Is应写在会出现在错误树里的具体错误类型上。 Is方法只做浅比较,不应在方法内部调用Unwrap。
errors.Is 的方向由第一个参数决定
我第一次写自定义错误匹配时,把 Is 放到了“目标错误”类型上,直觉是目标应该决定谁能匹配自己。结果方法完全没有触发。重新看官方定义后才发现,errors.Is 从来不是对两个值做对称协商:它把 err 当作待搜索的树,把 target 当作查询条件。
检查顺序可以概括为:当前错误与目标直接相等;若不相等,则调用当前错误自己的 Is(target);仍不匹配时,再按照 Unwrap() error 或 Unwrap() []error 展开子节点。多子节点按先序深度优先检查。

反向实现 Is 为什么没有效果
下面的目标类型即使把 Is 写成永远返回 true,也不会让普通源错误匹配成功,因为 GreedyTarget.Is 不在调用路径上:
type GreedyTarget struct{}
func (*GreedyTarget) Error() string { return "target" }
func (*GreedyTarget) Is(error) bool {
// 这个方法位于 target 一侧,errors.Is 不会反向调用它
return true
}
source := errors.New("source")
target := &GreedyTarget{}
// 结果仍为 false:source 不等于 target,也没有 source.Is(target)
matched := errors.Is(source, target)
这不是遗漏,而是 API 的方向性。若目标值也能主动决定匹配,错误树里的节点和查询目标会同时影响语义,调用方很难判断由谁负责兼容。标准库把责任放在被检查的错误节点上,因此一个错误类型可以声明“我等价于某个公共哨兵错误”。
把自定义 Is 写在产生错误的一侧
更常见的做法是让业务错误根据稳定字段匹配目标。下面的 CodeError 出现在返回链中,所以它的 Is 会被调用:
type CodeError struct {
Code string
Err error
}
func (e *CodeError) Error() string { return e.Code }
func (e *CodeError) Unwrap() error {
// 包装原因交给 errors.Is 统一遍历
return e.Err
}
func (e *CodeError) Is(target error) bool {
// 只比较当前节点和目标,不在这里递归展开错误链
t, ok := target.(*CodeError)
return ok && e.Code == t.Code
}
var ErrTimeout = &CodeError{Code: "timeout"}
func loadProfile() error {
cause := &CodeError{Code: "timeout"}
return fmt.Errorf("load profile: %w", cause)
}
err := loadProfile()
// 指针不相等,但错误树中的 CodeError.Is 会按 Code 匹配
if errors.Is(err, ErrTimeout) {
fmt.Println("可以按超时策略处理")
}

如果只需要匹配一个固定哨兵,也可以直接返回 target == ErrTimeout。若目标带字段,字段必须代表稳定兼容语义;不要把易变文案、时间或堆栈字符串当成匹配条件。
包装树与边界条件
| 位置 | errors.Is 的处理 |
|---|---|
| 当前节点 | 先做直接相等,再尝试当前节点的 Is(target) |
| target | 作为比较参数,不调用它的 Is,并应为可比较值 |
| 单个包装 | 通过 Unwrap() error 继续检查 |
| 多错误包装 | 通过 Unwrap() []error 深度优先检查子树 |
自定义 Is 返回 false 不会终止整棵树的搜索,标准库仍会继续展开该节点。也正因为遍历由外层统一完成,官方要求 Is 只做浅比较,不要自行调用两侧的 Unwrap。这样包装、连接错误和自定义等价关系才能组合,而不会重复递归。
交换 errors.Is 的两个参数可以吗?
通常不可以。交换后被搜索的错误树也变了,原错误链不会再被遍历,自定义方法的接收者也会改变。
target 必须是哨兵错误吗?
不必须,但它应是可比较且语义稳定的错误值。常见做法是包级哨兵,或只携带稳定匹配字段的目标对象。
自定义 Is 能同时保留底层原因吗?
可以。类型同时实现浅层 Is 和 Unwrap,前者表达等价关系,后者把底层原因交给标准库继续遍历。
PHP SensitiveParameter 怎么隐藏堆栈中的敏感值
- 上一篇
- PHP SensitiveParameter 怎么隐藏堆栈中的敏感值
- 下一篇
- 人类基准反应测试统计页怎么看?个人成绩、分布与排行榜入口说明
-
- Golang · Go问答 | 31分钟前 | flag · go ·
- Go flag.TextVar 默认值为什么必须实现 TextMarshaler
- 404浏览 收藏
-
- Golang · Go问答 | 53分钟前 | Go问答 · Go Unwrap errors.Join errors
- Go errors.Unwrap 为什么不支持多错误返回值
- 364浏览 收藏
-
- Golang · Go问答 | 2小时前 | 错误处理 · go · errors.Join errors
- Go errors.Join 为什么格式化后是多行文本
- 421浏览 收藏
-
- Golang · Go问答 | 3小时前 |
- Go DisallowUnknownFields 为什么只报告首个未知字段
- 261浏览 收藏
-
- Golang · Go问答 | 3小时前 | JSON · go · Go encoding/json json.RawMessage JSON编解码
- Go json.RawMessage 为什么需要复制后再长期保存
- 330浏览 收藏
-
- Golang · Go问答 | 3小时前 |
- Go json.Number Int64 为什么会返回语法错误
- 357浏览 收藏
-
- Golang · Go问答 | 4小时前 |
- Go json.Decoder More 为什么不能单独判断顶层还有数据
- 225浏览 收藏
-
- Golang · Go问答 | 4小时前 |
- Go csv.FieldsPerRecord 设为零为什么仍可能报错
- 301浏览 收藏
-
- Golang · Go问答 | 4小时前 | go · utf-8 ·
- Go csv.FieldPos 为什么使用字节列而不是字符列
- 389浏览 收藏
-
- Golang · Go问答 | 5小时前 |
- Go csv.InputOffset 为什么不是当前字段的字节位置
- 225浏览 收藏
-
- Golang · Go问答 | 5小时前 |
- Go csv.ReuseRecord 开启后为什么上一行内容会变化
- 240浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 240次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 289次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 255次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 238次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 47次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- go语言中的defer关键字
- 2023-02-17 150浏览
-
- Golang中Interface接口的三个特性
- 2023-01-07 394浏览
-
- go语言中函数与方法介绍
- 2023-01-07 297浏览
-
- go语言数据类型之字符串string
- 2022-12-30 321浏览

