当前位置:首页 > 文章列表 > Golang > Go问答 > Go errors.Join 返回的错误怎么逐个用 errors.Is 判断

Go errors.Join 返回的错误怎么逐个用 errors.Is 判断

来源:17golang原创 2026-09-09 06:06:42 0浏览 收藏

多个操作同时失败时,errors.Join 不会把错误变成只能按换行拆分的文本,而是返回一个包含多个子错误的错误树。判断其中是否有某个已知原因,直接对聚合结果调用 errors.Is 即可;只有需要把每个原因分别展示、统计或记录时,才读取 Unwrap() []error

最小写法是 errors.Is(joined, target)。它会检查聚合错误本身及其子错误,包括被 %w 包装的原因;不要解析 Error() 字符串,也不要用 errors.Unwrap 期待取出 Join 的切片。

errors.Join 为什么能被 errors.Is 命中

errors.Join 从 Go 1.20 开始提供多错误聚合能力。它会忽略传入的 nil,所有参数都是 nil 时返回 nil;非空结果实现的是 Unwrap() []error。标准库把这种结构视为错误树,errors.Is 会从当前节点开始检查,再按深度优先访问子节点。

package main

import (
    "context"
    "errors"
    "fmt"
)

func main() {
    errTimeout := context.DeadlineExceeded
    errCache := errors.New("缓存不可用")
    // 用 %w 保留缓存错误的可匹配关系,同时补充场景信息。
    joined := errors.Join(errTimeout, fmt.Errorf("读取用户资料:%w", errCache))

    // 业务分支按错误身份判断,不依赖 Error() 的显示文本。
    fmt.Println(errors.Is(joined, context.DeadlineExceeded)) // true
    fmt.Println(errors.Is(joined, errCache))                 // true
}
errors.Join 聚合错误、Unwrap 切片和 errors.Is 目标匹配的静态关系图
图1:聚合错误通过 Unwrap() []error 连接到多个原因,errors.Is 可以在这棵错误树中匹配目标。

这里两个判断都可能为 true,因为目标分别位于 Join 的不同分支。目标错误必须是可比较的;如果项目定义了自定义错误类型,也可以实现 Is(error) bool 来声明等价关系。判断成功只说明树中存在匹配项,并不表示所有操作都成功。

怎么对每个已知原因分别判断

“逐个判断”通常不是手写循环,而是对每个业务关心的目标各调用一次 errors.Is。例如批量刷新资料时,可以把超时、权限和缓存故障分别映射到不同处理策略:

func classify(err error) string {
    // 先判断更需要单独处理的业务原因。
    switch {
    case errors.Is(err, context.DeadlineExceeded):
        return "稍后重试"
    case errors.Is(err, errPermission):
        return "提示重新授权"
    case errors.Is(err, errCache):
        return "降级读取"
    default:
        return "记录未知错误"
    }
}

多个目标同时存在时,switch 只会返回第一个命中的分支;如果要同时收集标签,就分别执行判断:

func labels(err error) []string {
    labels := make([]string, 0, 3)
    // 每个目标独立判断,Join 中命中几个就保留几个标签。
    if errors.Is(err, context.DeadlineExceeded) {
        labels = append(labels, "timeout")
    }
    if errors.Is(err, errPermission) {
        labels = append(labels, "permission")
    }
    if errors.Is(err, errCache) {
        labels = append(labels, "cache")
    }
    return labels
}

不要用 err.Error() == "缓存不可用"strings.Contains 替代身份判断。错误文本可以随着上下文变化,errors.Is 才是包装和聚合关系的稳定入口。

需要逐个查看原因时怎么展开错误树

日志、指标或批量响应有时确实需要列出直接子错误。这时使用 Unwrap() []error 的接口断言。注意,errors.Unwrap(err) 只识别返回单个 errorUnwrap() error,对 errors.Join 会返回 nil

type multiUnwrapper interface {
    Unwrap() []error
}

func walk(err error, visit func(error)) {
    if err == nil {
        return
    }
    visit(err)

    // 先展开多错误节点;没有切片时再展开普通 %w 包装。
    if many, ok := err.(multiUnwrapper); ok {
        for _, child := range many.Unwrap() {
            walk(child, visit)
        }
        return
    }
    if one := errors.Unwrap(err); one != nil {
        walk(one, visit)
    }
}
外层错误树、Join 节点、多个子错误和普通 %w 包装的静态关系图
图2:需要展示每个原因时,区分 Join 的 []error 子节点与普通 %w 单链,避免错误地调用 errors.Unwrap。

遍历函数适合做日志明细或错误统计,不适合替代业务分支。遍历时要保留去重策略,否则同一个 sentinel 被多个包装层引用时,展示列表可能重复;而业务是否命中仍然交给 errors.Is

生产环境最容易踩到的四个边界

  • 全是 nil:errors.Join(nil, nil) 返回 nil,调用方可以直接按无错误处理。
  • 嵌套聚合:Join 的子节点还可以是另一个 Join,errors.Is 会继续检查嵌套树。
  • 展示和判断分离:Error() 适合给人看,errors.Is 适合程序判断;不要互相替代。
  • 顺序影响策略:一次只返回一个处理结果时,按业务优先级排列判断;要完整反映状态就独立收集多个命中结果。

速查结论:判断某个原因用 errors.Is(joined, target);判断自定义错误类别用 errors.As;读取 Join 的直接子节点用 Unwrap() []error;不要通过字符串切分恢复错误关系。

相关问题

errors.Join 和 fmt.Errorf 多个 %w 有什么区别?

两者都能形成包含多个子错误的错误树,errors.Iserrors.As 都可以继续匹配;选择时看你是要聚合已有错误,还是要在一段上下文文本中同时包装多个参数。

为什么 errors.Unwrap(errors.Join(...)) 是 nil?

因为 errors.Unwrap 只调用 Unwrap() error,而 Join 实现的是 Unwrap() []error。需要读取子错误时应做接口断言,或直接使用 errors.Is 进行判断。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL JSON_TABLE 的 FOR ORDINALITY 怎么保留数组原始序号MySQL JSON_TABLE 的 FOR ORDINALITY 怎么保留数组原始序号
上一篇
MySQL JSON_TABLE 的 FOR ORDINALITY 怎么保留数组原始序号
Redis XPENDING 怎么查看消费组中最老的未确认消息
下一篇
Redis XPENDING 怎么查看消费组中最老的未确认消息
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    37次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    189次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    129次使用
  • AI Prompt Library:免费AI提示词库,助力ChatGPT高效创作与营销
    AI Prompt Library
    探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
    53次使用
  • Generrated:DALL·E 2/3 AI绘画提示词灵感库与图像对比平台
    Generrated
    Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
    40次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码