首页 >  Golang >  Go问答

Go errors.Join 怎样判断其中是否包含指定错误

来源:17golang原创

时间:2026-10-28 22:10:05 175浏览 收藏

用 errors.Is(joined, target) 判断 errors.Join 合并结果是否匹配指定错误。Is会检查错误本身及其包装的子错误,普通的 joined == target 只比较最外层值,不能代替这项检查。目标错误应使用预先定义并复用的哨兵值,不要临时再创建一个同文案的错误。

官方地址:https://pkg.go.dev/errors

先定义一个批量检查小工具

假设一个资料清单检查器要同时检查名称和数量,希望一次展示所有问题。名称缺失与数量无效是两个独立错误类别,调用方既要得到合并错误,也要分别决定怎样提示用户。我们用包级哨兵保存类别,用%w附加字段上下文,再用Join合并。

errors.Join自Go 1.20加入标准库。读者准备支持该接口的Go环境,把后面的完整片段保存为main.go即可。示例只检查内存中的记录,不访问网站、数据库或文件,不需要额外依赖。

合并错误与两个字段错误包装关系的静态结构说明图
结构说明图:合并结果包含名称与数量两个分支,每个包装分支仍关联原哨兵;这里是错误组成关系,不是执行流程。

保留包装并用Is判断

package main

import (
    "errors"
    "fmt"
    "strings"
)

var (
    ErrNameMissing = errors.New("名称缺失")
    ErrCountInvalid = errors.New("数量无效")
)

type Record struct {
    Name string
    Count int
}

func checkRecord(r Record) error {
    var issues []error
    if strings.TrimSpace(r.Name) == "" {
        // 用%w保留错误身份,字段名只提供人类可读上下文。
        issues = append(issues, fmt.Errorf("字段name: %w", ErrNameMissing))
    }
    if r.Count 

这里两个字段检查都是示例业务规则,并非通用数据标准。重要的是错误关系:%w留下包装关系,Join保存分支,Is按目标查找。如果把%w换成只输出文本的%v,再把结果当普通文本错误返回,原哨兵身份就不在这条包装关系中了。

怎样运行并判断示例行为

# 在保存main.go的目录执行,让Go编译并运行这个内存检查器。
go run main.go

本文没有在本机执行示例,不附带实测日志。按代码中的输入,名称为空白、数量为0,所以两类判断都应成立。把输入改成非空名称与正数后,issues没有元素,Join返回nil,程序应走“记录通过检查”分支。若只改好一个字段,就只应提示另一类问题。

复现时观察分支是否符合输入,不要把多行错误文案的排版当作分类依据。Join的Error文本会把非nil子错误文案以换行组合,文案适合展示或记录;程序分支应依赖Is,而不是strings.Contains。

errors.Is目标哨兵与调用方提示关系的静态说明图
结构说明图:调用方用同一个哨兵值进行匹配,再决定对应提示;多个判断可同时成立,不暗示只存在一种失败原因。

接入调用方时保留哪些边界

需求适合方式误区
判断某一错误类别errors.Is(err, sentinel)比较合并根节点与哨兵是否相等
提取具体类型及字段errors.As把Is当作取出错误对象的方法
保留上下文和身份包装时使用%w只拼接错误文本后期待身份仍存在
多个类别同时提示独立的匹配判断else if让后面的错误类别被遮住

Is表达的是“树中存在匹配”。它不承诺列出所有命中节点,也不证明树中只含这一种错误。自定义错误可以实现Is方法来定义等价匹配,因此“包含指定错误”在这里应按匹配语义理解,不总是同一个对象引用。

Join会丢弃nil输入,全部为nil时返回nil;接口里承载的typed nil却不一定是接口nil,生产者仍要正确表达成功状态。不要靠Join自动修复错误的返回契约。

还有一个常见困惑:errors.Unwrap只处理单错误形式的Unwrap() error,不会替你展开Join的Unwrap() []error。它返回nil不能说明Join没有子错误。多数分类代码直接调用Is或As即可,不必自己写一套递归解析。

同样文案的errors.New为什么匹配不上?

每次New都会创建独立错误值,文案相同不代表身份相同。定义一次哨兵并在产生和判断时复用它,而不是在Is的第二个参数里再New一次。

只有一个非nil输入,可以直接用等号吗?

不要依赖Join只有一个输入时会返回原错误值这一假设。只要语义是判断包装树内是否匹配,就继续使用Is。

发现一种可忽略错误,能把整组错误都忽略吗?

不能只凭一次Is命中就丢弃整个合并结果,里面可能还有其他失败。如果需要逐条处理结果,保留结构化检查结果,或在明确契约下逐个分类,再决定整体状态。

这个小工具可以作为调用方分类的起点:先在产生处保存错误身份,再合并独立问题,最后按业务选择独立提示或明确的优先级。只把Join后的文案传给下游,就会丢失这种可判断能力。

参考:errors包的包装与匹配契约、Go 1.20多错误包装说明。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>