Go errors.Is 与 errors.As 设计可判断错误链
来源:17golang原创
时间:2026-10-16 09:32:11 474浏览 收藏
Go 设计可判断错误链时,可以用固定哨兵错误表达业务类别,用自定义错误类型携带字段,再通过 Unwrap 和 fmt.Errorf 的 %w 保留关系。调用方用 errors.Is 判断“属于哪一类”,用 errors.As 取得“哪种结构以及哪些字段”,不必解析错误字符串。
本文以导出任务的配额判断为例,比较不同表达方式的契约与代价,不涉及云服务选型,也不把错误文本当成稳定协议。官方文档入口:https://pkg.go.dev/errors;包装设计说明:https://go.dev/blog/go1.13-errors;格式化接口:https://pkg.go.dev/fmt#Errorf。
先明确调用方需要作出什么判断
假设导出服务需要拒绝超过配额的请求。页面层只想知道是否提示调整数量,诊断层还要知道请求量与上限,服务层则希望补充“准备导出时失败”的上下文。把这三种需求全塞进一条字符串,会让修改措辞变成调用方兼容性风险。
本例把需求限定为两个可判断点:ErrQuota 表示配额超限;QuotaError 携带 Limit 和 Requested。planExport 负责添加操作上下文,但保留已承诺的业务错误能力。它不读取真实账号或套餐,也不执行导出,数字只是用于说明返回结构。
文字、哨兵和错误类型分别承担什么
| 表达方式 | 适合承诺什么 | 需要承担的代价 |
|---|---|---|
| 普通错误文本 | 为日志提供可读说明 | 不能让调用方依赖固定措辞分支 |
| 哨兵错误 | 稳定的业务类别 | 需要统一使用同一个值并说明返回场景 |
| 自定义错误类型 | 字段、类型或能力 | 导出字段与类型形态成为兼容约定 |
| 包装错误 | 增加上下文且保留内部可判断能力 | 内部错误可能成为外部可观察契约 |
Is 不是错误文本比较器。errors.New 即使传入相同字符串,也会创建不同的错误值。As 也不是从字符串中解析字段;它沿可解包关系寻找匹配对象,把对象赋给目标变量。直接类型断言只看当前错误值,遇到外层包装就可能不匹配。
哨兵与错误类型不是二选一。只需要分类时没有必要为每个情况建立结构体;确实需要上限、字段名或操作信息时,自定义类型才更有价值。这个案例需要两者,所以让 QuotaError 包含 ErrQuota。
保留一条可解包的业务错误链
完整示例可以保存为 main.go,由读者自行运行。checkQuota 负责判断,planExport 负责包装,main 展示消费方式。本文没有执行代码,后面的结果描述均为按逻辑推导的预期。
package main
import (
"errors"
"fmt"
)
// 同一业务类别共用这一个值,不在每次调用时重新创建
var ErrQuota = errors.New("quota exceeded")
type QuotaError struct {
Limit int
Requested int
cause error
}
func (e *QuotaError) Error() string {
// 文本给人阅读,程序不要解析这里的措辞
return fmt.Sprintf("requested %d, limit %d", e.Requested, e.Limit)
}
func (e *QuotaError) Unwrap() error {
return e.cause // 让调用方仍能判断业务类别
}
func checkQuota(requested, limit int) error {
if requested
链的外层是带操作说明的包装错误,内部是 QuotaError,再内部是 ErrQuota。QuotaError 的 Error 方法只负责文本,Unwrap 才是保留内部关系的入口。如果只在 Error 中拼接内部错误内容,却不提供包装关系,Is 无法凭那些文字找到哨兵。

checkQuota 成功时返回真正的 nil,planExport 只在 err 非 nil 时包装。不要用一个内部指针为 nil 的错误接口冒充成功,也不要在成功路径上无条件 fmt.Errorf。一个错误接口是否为 nil,取决于其动态类型和值,而不只是内部指针。
调用方同时取得类别与结构化信息
以请求 5、上限 3 为输入,预期 quota 为 true,As 得到 Requested=5、Limit=3。新建的同文案 errors.New 不应匹配原哨兵,textOnly 也不应保留 ErrQuota 的判断能力。这些对照用于说明契约,不表示真实业务配额或运行测量。
var detail *QuotaError 后传入 &detail 容易让初学者犹豫:这里需要的是“目标变量的地址”。detail 本身是一个等待被赋值的 *QuotaError,As 需要能够修改这个变量,因此传入其地址。匹配成功后才读取 detail 的字段;不要忽略布尔返回值。
本例的 Error 使用指针接收者,所以 *QuotaError 实现 error。把 target 写成不匹配的值类型并不会自动变成同一种类型;不合法的 target 还可能引发 panic。最好让返回类型、接收者和目标变量三者保持一致。

Is 和 As 可以同时匹配同一个错误。若业务需要“优先按类别处理,同时读取诊断字段”,可以独立调用两者;不要机械放在互斥分支里,然后把其中一项的信息丢掉。As 找到的是匹配对象,不是克隆副本,消费层一般应只读这些信息,避免修改共享错误对象。
包装之前先决定公开哪些底层细节
%w 不应该成为所有错误返回的自动动作。只要把底层驱动错误包装出去,调用方就可能通过 Is 或 As 观察它;日后更换驱动时,行为也可能随之改变。这是包装方案的重要取舍:上下文透明度更好,兼容责任也更大。
在本案例里,服务层公开的是自己定义的 ErrQuota 与 QuotaError,而不是数据库或网络库的私有实现。如果服务选择隐藏依赖,应在边界把底层错误映射成承诺的业务错误,并在内部另行记录诊断信息。不要简单认为换成 %v 就完成了保密:底层错误文本仍可能包含敏感内容,外部响应应使用另外设计的安全文案。
也不要为了方便提取字段,把 Token、请求原文或个人数据直接放进 Error 文本。本例只有合成的计数信息。需要程序判断的字段与要展示给用户的文字,是两个不同的设计决定。
让错误契约在后续重构中保持稳定
为可判断错误链确定三个约定即可:哪些失败必须匹配某个哨兵,哪些类型及字段允许调用方读取,哪些内部错误不会公开。添加包装层可以改变可读文本,却应保留已经承诺的类别与类型能力;主动取消包装则可能构成行为变化。
读者可以为示例补充成功、负参数、配额超限和多层包装四类测试,断言 Is/As 结果及字段,而不是把完整错误文案锁死。本例的负参数错误不属于 ErrQuota;如果未来需要独立分类,应明确新增契约,不能悄悄复用超限哨兵。
errors.Is 能代替所有 err == nil 吗?
正常成功判断继续使用 err == nil。Is 的主要用途是匹配特定错误目标,不需要把每个基础判断都改成遍历错误关系。
只有文本相同,为什么 Is 返回 false?
默认匹配不是字符串相等。不同 errors.New 返回值即使描述一致也不是同一个哨兵。应该复用声明好的 ErrQuota,而不是在判断时临时 new 一个。
自定义 Error 方法之后还必须写 Unwrap 吗?
如果这个类型只表达自身,就不必包装任何内部错误;如果它承诺保留内部原因,则需要建立对应可解包关系。本例要让 ErrQuota 可见,所以实现 Unwrap。
有关类型提取的具体要求见 errors.As 文档,包装是否构成公开承诺的讨论见 Go 官方错误设计文章,%w 的语义见 fmt.Errorf 文档。稳定的错误处理来自清晰的契约,而不是一串永远不变的报错文字。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
380 收藏
-
323 收藏
-
394 收藏
-
281 收藏
-
245 收藏
-
434 收藏
-
317 收藏
-
267 收藏
-
457 收藏
-
460 收藏
-
446 收藏
-
311 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习