当前位置:首页 > 文章列表 > Golang > Go教程 > Go errors.Is 与 errors.As 设计可判断错误链

Go errors.Is 与 errors.As 设计可判断错误链

来源:17golang原创 2026-10-16 09:32:11 0浏览 收藏

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 无法凭那些文字找到哨兵。

外层包装、结构化错误与哨兵类别的静态包含关系
图1:原创静态说明图。操作上下文与业务错误分组说明各对象的归属和包含关系,不是执行流程或实测输出。

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。最好让返回类型、接收者和目标变量三者保持一致。

同一错误对象分别供类别判断和字段提取使用的静态关系
图2:原创静态说明图。类别判断与字段提取共享同一输入错误,但承担不同的读取目的,不代表先后执行要求。

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 文档。稳定的错误处理来自清晰的契约,而不是一串永远不变的报错文字。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go go list 提取构建依赖信息的用途Go go list 提取构建依赖信息的用途
上一篇
Go go list 提取构建依赖信息的用途
Go errors.Join 聚合并发任务错误的返回策略
下一篇
Go errors.Join 聚合并发任务错误的返回策略
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    489次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    546次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    570次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    524次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    337次使用