预计将于 2023 年 2 月发布的 Go 1.20 有一个小的变化,对于那些大量使用错误包装的应用程序来说,可能会有效改进它们的错误处理方法。
让我们看一下它的用法,但首先,需要简要回顾一下什么是错误包装。如果你已经掌握了可以直接跳到下面的 “Go 1.20 新功能” 部分以获取新的信息。
(资料图片)
Go 中的错误是实现一个非常简单的接口:
typeerrorinterface{Error()string}
错误类型可以是任何东西,从string本身到int,但通常它们是struct类型。下面这个例子来自标准库:
typeerrstruct{sstring}func(e*err)Error()string{returne.s}
要检查 Go 中的错误,你只需比较一个值(在本例中为int值):
iferr==io.EOF{//...}
第二种常见的用法是检查错误的类型,那也意味着要写更多的代码:
ifnerr,ok:=err.(net.Error){//...(usenerrwhichisanet.Error)}
在上面的例子中,类型断言测试类型net.Error的err值,并创建一个新变量nerr,它可以在 if 语句中使用。Go 中的错误方便理解、易于使用且非常高效。
错误包装从 Go 1.13 开始,引入了错误包装。包装允许将错误嵌入到其他错误中,就像在其他语言中包装异常一样。这非常实用,比如函数遇到 “record not found” 错误时,可以向错误信息中添加更多上下文信息,例如 “unknown user: record not found”。
Go 中错误包装设计背后的有趣想法是:契约不用关心错误类型、结构或它们是如何创建的。而唯一关注的是解包过程和转换为字符串,因为这两者是必须的。这就非常容易实现:支持解包的错误类型必须实现Unwrap() error方法。
标准库中没有(命名的)接口可以向您展示,因为接口是隐式实现的,没有必要单独写一个。这里我们写一个只是为了更好说明这篇文章:
typeWrappedErrorinterface{Unwrap()error}
我们来看看 Go 标准库(实际上是 package fmt)中是如何实现包装错误的:
typewrapErrorstruct{msgstringerrerror}func(e*wrapError)Error()string{returne.msg}func(e*wrapError)Unwrap()error{returne.err}
由于上面错误类型实现了Error() string方法,所以说 Go 中的错误实际上最终是字符串并没有错,因此需要一种创建这些字符串的良好机制。这就是标准库中的函数fmt.Errorf发挥作用的地方:
varRecordNotFoundErr=errors.New("notfound")constname,id="lzap",13werr:=fmt.Errorf("unknownuser%q(id%d):%w",name,id,recordNotFoundErr)fmt.Println(werr.Error())
一个特殊格式的动词%w,每次调用只能使用一次(稍后会详细介绍),用于错误参数。除此之外,该函数的工作方式类似于fmt.Printf函数。下面的例子打印了这个结果:
unknownuser"lzap"(id13):notfound
如你所见,错误包装本质上是一个链表。要解包错误,请使用errors.Unwrap函数,该函数将为链表中的最后一个错误值返回nil。要检查错误类型或值,需要遍历整个列表,这对于需要进行频繁的错误检查不太实用。幸运的是,有两个辅助函数可以做到这一点。
检查包装错误列表中的值:
iferrors.Is(err,RecordNotFoundErr){//...}
检查特定类型(下面例子是来自标准库的网络错误):
varnerr*net.Erroriferrors.As(err,&nerr){//...(usenerrwhichisa*net.Error)}
以上总结了 Go 1.13 及更高版本中的错误包装。
Go 1.20 新特性让我们看看 Go 1.20 中真正的新功能,从函数errors.Join开始,它通过可变参数包装错误列表:
err1:=errors.New("err1")err2:=errors.New("err2")err:=errors.Join(err1,err2)fmt.Println(err)
当事先不知道错误数量时,此功能可用于将错误连接在一起。一个很好的例子是从 goroutines 收集错误。值得一提的是,该函数将列表中的错误与换行符连接起来。上面的代码片段打印:
err1err2
对于许多应用程序或(日志记录)库来说,这可能会存在问题,它们期望错误通常只是没有换行符的字符串。幸运的是,Go 1.20 中的另一个变化改变了fmt.Errorf的行为:该函数现在接受多个%w格式说明符:
err1:=errors.New("err1")err2:=errors.New("err2")err:=fmt.Errorf("%w+%w",err1,err2)fmt.Println(err)
以前会导致格式错误的格式字符串现在可以正确打印:
err1+err2
同时包装多个错误实现Unwrap() error,这是可能的吗?
事实证明,在 Go 1.20 标准库中有一种新的机制: 实现Unwrap() []error函数的错误类型可以包装多个错误。让我们来看看这是如何在库中实现的:
typejoinErrorstruct{errs[]error}func(e*joinError)Error()string{//concatenateerrorswithanewlinecharacter}func(e*joinError)Unwrap()[]error{returne.errs}
一个理论上的接口,但标准库中实际不存在,如下所示:
typeMultiWrappedErrorinterface{Unwrap()[]error}
由于 Go 不允许方法重载,因此每种类型都可以实现Unwrap() error或Unwrap() []error,但不能同时实现。还记得我提到过包装错误本质上是一个链表吗?实现前一个(新引入的)方法的类型实际上形成了一个链接树,函数errors.Is和errors.As的工作方式相同,只是现在它们需要遍历树而不是列表。根据文档,该实现执行预排序、深度优先遍历。
这确实是 Go 1.20 带来的全部,它可能看起来是一个小的变化,但它提供了如何有效和干净地处理错误的新方法。在展示真实示例之前,让我总结一下新功能:
新的Unwrap []error函数契约允许遍历错误树。
新的errors.Join函数,这是一个方便的函数,用于连接两个错误字符串值(使用换行符)。
现有函数errors.Is和errors.As已更新,可以同时处理错误列表和错误树。
现有函数fmt.Errorf现在接受多个%w格式动词。实践上面这一切都很棒,但是你如何在实践中利用它呢?
在一个小型 REST API 微服务中,我们通过errors.New和fmt.Errorf处理来自 DAO 包(数据库)、REST 客户端(其他后端服务)和其他包的各种错误。返回的 HTTP 状态代码应该是 2xx、4xx 或 5xx,具体取决于错误状态以遵循最佳 REST API 实践。实现此过程的一种方法是解开主 HTTP 处理程序中的错误并找出它是哪种错误。
然而,通过多重错误包装,现在可以包装根本原因(例如数据库返回 “no records found” )和返回给用户 HTTP 代码(在本例中为 404)。
一个工作示例如下所示:
packagemainimport("errors""fmt")//commonHTTPstatuscodesvarNotFoundHTTPCode=errors.New("404")varUnauthorizedHTTPCode=errors.New("401")//databaseerrorsvarRecordNotFoundErr=errors.New("DB:recordnotfound")varAffectedRecordsMismatchErr=errors.New("DB:affectedrecordsmismatch")//HTTPclienterrorsvarResourceNotFoundErr=errors.New("HTTPclient:resourcenotfound")varResourceUnauthorizedErr=errors.New("HTTPclient:unauthorized")//applicationerrors(thenewfeature)varUserNotFoundErr=fmt.Errorf("usernotfound:%w(%w)",RecordNotFoundErr,NotFoundHTTPCode)varOtherResourceUnauthorizedErr=fmt.Errorf("unauthorizedcall:%w(%w)",ResourceUnauthorizedErr,UnauthorizedHTTPCode)funchandleError(errerror){iferrors.Is(err,NotFoundHTTPCode){fmt.Println("Willreturn404")}elseiferrors.Is(err,UnauthorizedHTTPCode){fmt.Println("Willreturn401")}else{fmt.Println("Willreturn500")}fmt.Println(err.Error())}funcmain(){handleError(UserNotFoundErr)handleError(OtherResourceUnauthorizedErr)}
这将打印:
Willreturn404usernotfound:DB:recordnotfound(404)Willreturn401unauthorizedtocallotherservice:HTTPclient:unauthorized(401)
从这样的人工代码片段中可能看起来不太明显的是,实际上的错误声明通常分布在许多包中,并且不容易跟踪所有可能的错误以确保所需的 HTTP 状态代码。在这种方法中,所有在一个地方声明的应用程序级包装错误也包含了 HTTP 代码。
请注意,这在 Go 1.19 或更早版本中是不可能的,因为fmt.Errorf函数只会包装第一个错误。该代码确实在 1.19 上可以编译,甚至不会产生运行时恐慌,但它实际上不会工作。
显然,常见的 HTTP 状态代码很容易成为一种新的错误类型(基于int类型),因此可以通过errors.As轻松提取实际代码,但我想让示例保持简单。
Feel free to play around with the code on Go Playground. Make sure to use “dev branch” or 1.20+ version of Go. 可以在 Go Playground 上自由运行上述代码。确保使用 “dev branch” 或 Go 的 1.20+ 版本。现有应用在你的应用程序中实施新功能时,请注意errors.Unwrap函数。对于具有Unwrap() []error的错误类型,它总是返回nil:
err1:=errors.New("err1")err2:=errors.New("err2")err:=errors.Join(err1,err2)unwrapped:=errors.Unwrap(err)fmt.Println(unwrapped)
由于 Go 1.X 兼容性承诺,这会打印出 “nil”。当你引入多个包装错误时,请确保检查展开代码。幸运的是,典型 Go 代码中的大部分错误检查都是使用errors.Is和errors.As完成的。
错误包装并不是 Go 中所有错误处理的最终解决方案。它只是提供了一种干净的方法来处理典型 Go 应用程序中的错误,对于简单应用程序来说也许就完全足够了。原文地址:https://lukas.zapletalovi.com/posts/2022/wrapping-multiple-errors/原文作者:Lukáš Zapletal本文永久链接:https://github.com/gocn/translator/blob/master/2022/w50_Wrapping_multiple_errors译者:haoheipi校对:watermelo
往期推荐
谷歌发布查找开源漏洞的Go工具OSV-Scanner最好的Go框架:没有框架?
「每周译Go」如何在Go中构造For 循环想要了解Go更多内容,欢迎扫描下方关注公众号,回复关键词 [实战群],就有机会进群和我们进行交流
分享、在看与点赞Go
-
GO 1.20 新功能:多重错误包装-世界信息预计将于2023年2月发布的Go1 20有一个小的变化,对于那些大量使用错误包装的应用程序来说,可能会有效改进它们的错误处理方法。让我们看一下它的
-
CQF考试费用多少?考试的收费项目有哪些? 快讯CQF考试费用并不是固定的,且在不同的国家在报考CQF的费用上也会有所差别。如在美国的CQF报考费在2万美金左右;在英国和欧洲不包括增值税的
-
最新快讯!超越ConvNeXt!Transformer 风格的卷积网络视觉基线模型Conv2Former↑点击蓝字关注极市平台作者丨科技猛兽编辑丨极市平台极市导读本文提出一种卷积调制模块,利用卷积来建立关系,这比注意力机制在处理高分辨率
-
天天资讯:2022年湟中区高校毕业生就业直播带岗活动时间+对象一、活动名称:2022年湟中区人力资源市场高校毕业生就业服务周直播带岗活动二、活动主题:启航新征程就业向未来三、活动时间2022年12月29日晚
-
2021年我国GDP最终核实为1149237亿元-世界快消息GDP是国民经济核算的核心指标,也是衡量一个国家经济状况和发展水平的重要指标。按照我国GDP核算和数据发布制度规定,年度GDP核算包括初步核算
-
我国知识产权保护工作取得了长足进步今天(12月27日),中国贸促会举行新闻发布会,对外发布了《2022年全球知识产权保护指数报告》。
-
基层河湖管护看四川——成都高新区桂溪街道交子公园社区:当前速讯4 5公里的碧水从成都高新区桂溪街道交子公园社区穿城而过,她就是成都人的“母亲河”——锦江!多年来,各级政府重拳出击对锦江流域水生态...
-
全球观热点:CFA报名后可以不要教材吗?cfa怎么备考?CFA报名后可以不要教材,要不要教材都取决于考生的选择。如果考生选择电子版教材就不需要单独支付教材费用,如果选择纸质版教材就需要支付一定
-
头条焦点:美国VOX网站:如何让新年目标可实现非常具体地说明如何实现目标:米尔克曼说,目标的问题通常在于它们太抽象:我要更有耐心、我要多做志愿者、我要多省钱
-
速看:多名球员缺席 北汽女排第二阶段首战落败原标题:多名球员缺席(引题)北汽女排第二阶段首战落败(主题)北京晚报讯(记者卓然)昨天,2022至2023赛季中国女排超级联赛第二阶段在江西
-
满易贷逾期25天会不会上征信网贷逾期一般会上征信,有些借贷机构在用户逾期后一天后就会上报给征信机构,而有些借贷机构则是会在几天后上报给征信机构,因为有些借贷机构可
-
14名伊朗渔民遭极端组织扣押近8年后获释:天天消息新华社德黑兰12月25日电(记者高文成)伊朗塔斯尼姆通讯社25日援引伊外交部消息说,遭极端组织索马里“青年党”武装分子扣押近8年的14名伊朗...
-
每日速讯:新鲜柠檬蜂蜜水怎么做 新鲜柠檬蜂蜜水如何制作美味好喝1、原料:柠檬、蜂蜜、盐、清水。2、做法步骤:准备蜂蜜、柠檬。柠檬用盐搓洗干净,在清水里泡十分钟。3、用干净的菜板,刀把柠檬切片。把柠檬
-
美联新材:公司将按相关规定披露股东的减持进展情况,敬请关注后续相关公告-今日视点美联新材(300586)12月24日在投资者关系平台上答复了投资者关心的问题。投资者:贵公司的股东已经减持了多少股份,是否告知?美联新材董秘:您
-
天普股份: 关于宁波市天普橡胶科技股份有限公司2022 年持续督导工作现场检查报告 热文天普股份:关于宁波市天普橡胶科技股份有限公司2022年持续督导工作现场检查报告
-
天天观察:水煮带壳花生需要煮多久 水煮带壳花生怎么做1、水煮带壳花生一般需要20-30分钟,具体由花生的量、锅具等而定。水煮花生一般是用新鲜的花生,如果是使用电压锅煮,一般20分钟左右就可以了
-
一起闯关,天地更宽!疫情防控进入新阶段。眼下正是闯关时。既为闯关,定是不易。三年抗疫,我们已经闯过一关又一关。关关难过关关过。秘诀之一,就是上下同心、守
-
有配偶者与他人同居需要承担什么法律后果?1、如果在离婚诉讼中,一方有证据证明另一方存在“有配偶者与他人同居”的情形,会产生以下法律后果:无过错一方坚决要求离婚的,法院应当...
-
环球快报:2022衢州一级工伤赔偿标准如何认定职工因工致残被鉴定为一级至四级伤残的,保留劳动关系,退出工作岗位,享受以下待遇:(一)从工伤保险基金按伤残等级支付一次性伤残补助金,
-
变压器综合在线监测系统在河北雄安新区应用:热讯12月19日,在河北雄安新区供电公司智能运检管控中心内,运检人员通过变压器综合在线监测系统采集识别220千伏昝西变电站变压器的声纹信号。该系
-
英力股份股东舒城誉铭减持12.15万股套现193万2022年前三季度公司净利2309.52万挖贝网12月21日,英力股份(300956)近日发布公告,公司股东舒城誉铭股权管理中心(有限合伙)以集中竞价交易方式减持公司股份12 15万股,占公
-
*ST荣华(600311)12月21日主力资金净卖出514.35万元截至2022年12月21日收盘,*ST荣华(600311)报收于1 33元,下跌5 0%,换手率2 44%,成交量16 25万手,成交额2187 15万元。12
-
全球即时看!明德生物:公司新冠抗原检测产品有在京东开设自营店铺进行销售,可自行购买(原标题:明德生物:公司新冠抗原检测产品有在京东开设自营店铺进行销售,可自行购买)同花顺(300033)金融研究中心12月21日讯,有投资者向
-
方城法院对违反人身安全保护令者进行训诫-天天热讯近日,方城县人民法院发出2022年首份人身安全保护令,对违令者朱某依法进行训诫,确保人身安全保护落到实处。据了解,妻子章某某与丈夫朱某某
-
颜值与内涵并存 博物馆﹃日历大战﹄开启随着时针嘀嗒嘀嗒,2022年“余额不足”,各家博物馆的“日历大战”又来了。故宫博物院、中国国家博物馆、陕西历史博物馆……各大博物馆(院...
-
【手慢无】超划算!支持高温煮洗的小米洗衣机 当前焦点文|阳光贝拉总编|唐迪这款洗衣机近期只要849元,还送一个双肩包,如果是新客,还能再减30元,到手819元。其实对于一些家庭里有宝宝或者女性的
-
上机数控董秘回复:公司在硅片切割过程中,充分结合自身的需求情况来选择钨丝和金刚线,目前阶段两种都有使用-天天视讯上机数控(603185)12月19日在投资者关系平台上答复了投资者关心的问题。投资者:贵公司在硅片切割行业地位超前,有用到细钨丝吗还是坚持用金刚
-
每日热议!纳芯微:12月16日融券卖出金额519.41万元,占当日流出金额的8.08%同花顺数据中心显示,纳芯微12月16日获融资买入581 46万元,占当日买入金额的8 21%,当前融资余额7491 85万元,占流通市值的0 96%,超过历史50
-
还呗网贷逾期6年多久上征信系统网贷逾期一般会上征信,有些借贷机构在用户逾期后一天后就会上报给征信机构,而有些借贷机构则是会在几天后上报给征信机构,因为有些借贷机构可