Go errors.Is 的自定义错误判等方法
来源:17golang原创
时间:2026-10-27 21:26:30 310浏览 收藏
Go 自定义错误只要实现 Is(target error) bool,就能为 errors.Is 增加分类匹配规则。该方法应只比较当前错误与目标;包装关系由标准库遍历。这里的“判等”是判断错误是否符合约定分类,不要求两个对象拥有相同地址,也不代表业务字段全部相等。
官方地址:https://pkg.go.dev/errors
确定目标:先区分错误身份与业务分类
面对“资源名不同,但都属于配额不足”的场景,我会先把调用方真正需要的问题写清楚:它想知道应否降低请求量,而不是比较两条报错文案。资源名称、限制值放进具体错误;稳定分类交给一个共享的 ErrQuota 标识。这样的分工使提示文字可以调整,分支判断仍保持原意。
errors.Is 会检查传入错误及其包装树,既能使用相等比较,也能使用当前错误实现的 Is 方法。若只是返回、包装同一个哨兵错误,通常不必自定义 Is;只有需要把有自身类型和字段的错误映射到某个分类时,才值得增加这层规则。以下示例属于接口设计示例,没有把示意结果当作本机执行记录。
设计阶段:让具体错误匹配稳定标识
先定义一次 ErrQuota,再定义 QuotaError。Resource 和 Limit 解释失败背景;Is 只认可 ErrQuota,不拿错误字符串作分类,不把所有非空错误都算作配额错误。示例还处理了空接收者,避免业务分类方法自身解引用空指针。
package main
import (
"errors"
"fmt"
)
// 分类标识只声明一次,调用方复用它。
var ErrQuota = errors.New("quota exceeded")
type QuotaError struct {
Resource string
Limit int
}
func (e *QuotaError) Error() string {
// 防御性处理 typed nil;正常业务失败始终返回非空指针。
if e == nil {
return ""
}
return fmt.Sprintf("%s quota exceeded (limit=%d)", e.Resource, e.Limit)
}
func (e *QuotaError) Is(target error) bool {
// 只比较当前错误的分类,不在方法内遍历包装树。
return e != nil && target == ErrQuota
}
func reserveSlots(requested, limit int) error {
if requested > limit {
return &QuotaError{Resource: "worker slots", Limit: limit}
}
// 成功时返回真正的 nil 接口。
return nil
}
func submit() error {
if err := reserveSlots(7, 4); err != nil {
// %w 保留底层错误,调用方仍可识别分类。
return fmt.Errorf("submit task: %w", err)
}
return nil
}
func main() {
err := submit()
if err == nil {
fmt.Println("accepted")
return
}
if errors.Is(err, ErrQuota) {
// 匹配到业务分类后给出提示;这里不自动重试。
fmt.Println("reduce requested slots")
return
}
fmt.Println("unexpected error:", err)
}
这段代码中的配额限制是示例业务约定,并非标准库规定。reserveSlots 在成功时直接返回 nil,失败时才构造 QuotaError;调用方通过分类决定提示内容。按照方法定义,具体的非空 QuotaError 能匹配 ErrQuota,Resource 和 Limit 不参与这一分类比较。

衔接阶段:保留包装关系,让外层调用方分类
submit 使用 fmt.Errorf 的 %w 保存底层错误。调用方取得的是带上下文的外层错误,仍使用 errors.Is(err, ErrQuota)。如果改用 %v,仅把文字放进新错误,包装树不包含原来的 QuotaError,原有分类能力就不会通过这层文字自动恢复。
自定义 Is 返回 false,并不要求标准库停止搜索。若当前错误还有 Unwrap 暴露的子错误,errors.Is 仍可检查它们。因此,Is 不能作为“屏蔽底层某类错误”的过滤器。本文的 QuotaError 没有底层原因,也无需为了分类额外伪造 Unwrap。
我会把接入过程分成三个小目标:先约定分类含义,再在错误生产处保留具体字段,最后让消费处只依赖需要的分类。若消费方确实需要 Resource、Limit 等数据,另用 errors.As 提取相应类型;errors.Is 的布尔值不会把字段交给调用方。

落地阶段:明确方向、空值与比较范围
匹配有方向。errors.Is(err, target) 询问 err 一侧错误树中的对象是否匹配 target,并不自动调用 target.Is(err)。不能因为具体 QuotaError 能匹配 ErrQuota,就推导出反向调用也一定成立。
不要递归调用自己。在 Is 方法中再次执行 errors.Is(e, target),会重新进入同一方法。也不要手工拆解任一侧的包装关系;浅比较当前对象与目标,把遍历留给外层标准库函数。
别用相同文案重新创建目标。errors.New 每次产生独立错误值,因此另写一个相同文字的 errors.New,并不等于共享的 ErrQuota。示例的 Is 正是通过这个共享标识建立契约。
typed nil 不是成功值。把值为 nil 的 *QuotaError 装入 error 接口后,接口本身通常仍不等于 nil。示例中的空接收者保护只避免方法出错,不能把这样的接口改成真正的 nil。成功路径应明确 return nil。
目标使用可比较的错误值。本文的目标是 errors.New 返回的指针型错误标识,适合直接比较。若想按业务字段匹配另一个复杂错误,应先明确字段含义、做类型判断,再比较必要字段;不要把任意 error 的整体相等当成万能协议。
交付契约:给调用方一张速查表
| 任务 | 本文做法 | 接口含义 |
|---|---|---|
| 判断配额分类 | errors.Is(err, ErrQuota) | 寻找符合分类的错误 |
| 保留失败上下文 | fmt.Errorf 配合 %w | 外层文字与底层原因共同保留 |
| 实现自定义分类 | Is(target error) bool | 仅对当前对象作浅比较 |
| 读取具体错误字段 | errors.As | 取得类型对象再读取字段 |
| 正常成功 | return nil | 不返回装有空指针的 error |
推荐把文档里的承诺写成“哪些具体失败能被哪些公开标识匹配”,而不是“错误内容相同就相等”。自定义 Is 一旦成为调用方的分支依据,修改它的分类规则就可能改变上层处理,应与公开接口一样慎重维护。
只实现 Error 方法够不够?
Error 只提供文字。共享哨兵的直接返回或包装可以依靠默认匹配;需要自定义分类映射时,再实现 Is。
是否应该把所有字段相同的错误都匹配成功?
取决于业务契约。本文只回答配额分类,不承诺字段等价,也不把 Resource 的空字符串设计成通配符,避免调用方误解匹配范围。
为什么外层报错内容不同仍能命中?
因为 %w 保留了底层错误对象,errors.Is 检查包装关系,不依靠外层字符串相同。
技术依据:Go errors.Is 官方接口说明,以及同页 New、As 的接口定义。代码、业务字段与两张说明图为本文原创。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
161 收藏
-
225 收藏
-
248 收藏
-
138 收藏
-
492 收藏
-
238 收藏
-
109 收藏
-
318 收藏
-
409 收藏
-
464 收藏
-
352 收藏
-
125 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习