首页 >要闻 > > 正文

热消息:GO 1.20 新功能:多重错误包装

2022-12-27 14:16:07

预计将于 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

上一篇: 下一篇:
x
推荐阅读

热消息:GO 1.20 新功能:多重错误包装

2022-12-27

航天电器(002025)12月26日主力资金净卖出1190.30万元

2022-12-27

罗平锌电(002114.SZ):富乐铅锌矿取得新采矿许可证并于即日起复产

2022-12-26

全球微资讯!圣达生物(603079)12月26日主力资金净买入123.80万元

2022-12-26

异动快报:天顺股份(002800)12月26日9点25分触及跌停板 天天热讯

2022-12-26

新成昆铁路开通后西昌花卉产业迎来新气象

2022-12-25

桔子分期贷款逾期六天多久会上征信系统

2022-12-24

注意!新冠康复后,这件事先别急着做——-环球动态

2022-12-24

焦点日报:资阳安岳:柠檬俏销市场 线下线上订单猛增

2022-12-23

每日信息:支持开票 | Python实证指标构建与文本分析

2022-12-23

【世界新要闻】奇才用八村报价克劳德遭拒 太阳想换首发级别球员

2022-12-23

每日速讯:大湖股份: 大湖水殖股份有限公司独立董事关于第八届董事会第二十五次会议相关事项的独立意见

2022-12-22

非法买卖部队证件,买卖双方均获刑 全球今热点

2022-12-22

网贷逾期44年暂时还不起会怎么样 天天滚动

2022-12-22

环球聚焦:美国驻北约大使鼓吹中俄破坏北约统一,外交部回应

2022-12-21

美众议院要求公布特朗普近6年税表详细内容 世界讯息

2022-12-21

动态:漫步者: 防范大股东及关联方资金占用专项制度

2022-12-21

大北农跌7.12%,天风证券一个月前给出“买入”评级,目标价16.00元

2022-12-20

全球讯息:甬矽电子董秘回复:公司产品的毛利率受产业周期、原材料采购价格、工艺技术、客户结构等多种因素的影响

2022-12-20

业务流程建模,细到几层?

2022-12-19

31省份累计报告接种新冠病毒疫苗345749.1万剂次-当前动态

2022-12-19

仁和药业董秘回复:只要是对公司有利的商业行为,在合法合规前提下,公司都秉持积极的心态去操作

2022-12-19

平桥区:线上调解“云办案” 司法为民“在指尖”_每日头条

2022-12-18

被判死刑缓刑是什么意思,有重大立功表现的怎样办, 天天快报

2022-12-18

关注:江湛线列车推出“小候鸟”乐园服务 营造良好乘车氛围

2022-12-17

佐力药业: 关于聘任公司高级管理人员的公告

2022-12-16

楚环科技(001336)12月16日主力资金净卖出412.71万元

2022-12-16

润阳科技(300920)12月15日主力资金净买入397.55万元-动态焦点

2022-12-16

商丘交警一大队火车站中队组织外卖企业开展“一盔一带”宣传活动 全球讯息

2022-12-15

凯盛新材董秘回复:截止2022年12月9日,公司股东人数为22216人-速讯

2022-12-15

今日热文:华致酒行(300755)12月14日主力资金净买入1531.69万元

2022-12-15

恒华科技获40家机构调研:公司目的不在于持有新能源资产,而是专注于运用自主可控的工业软件平台在新能源资产数字化运维方面的服务,来获得持续稳健收益和利润(附调研问答)|焦点简讯

2022-12-14

泰瑞机器:泰瑞机器是注塑机制造商及智能注塑解决方案提供商

2022-12-14

资讯推荐:软中华多少钱一条价格表(新版扁盒中华100元叫什么)

2022-12-14

当前要闻:东海证券:线下家电门店客流有望渐进恢复

2022-12-13

【环球报资讯】欧盟盯上Teams 微软拟让步避免反垄断调查

2022-12-13

莱茵生物(002166):公司董事减持计划时间过半未减持股份 全球动态

2022-12-12

天天速递!助企纾困加力 多地落实落细系列税费支持政策

2022-12-12

全球热文:广州出台18条措施加快先进制造业项目投资建设

2022-12-10

焦点简讯:Vail Resorts(MTN.US):2023年Q1财报实现营收2.794亿美元

2022-12-09

复旦微电: 关于2021年限制性股票激励计划首次授予部分第一个归属期归属条件成就的公告

2022-12-07

世界观察:万朗磁塑(603150):于公司会议室举行投资者关系活动

2022-12-06

山东推出文化体验主题旅游产品 推出多种旅游惠民措施

2022-05-22

唐山加快验收时间 为建筑工地开复工保驾护航

2022-03-19

消费者“身材焦虑” 减肥市场疯狂吸金

2022-03-19

2月份秦皇岛新建商品住宅销售价格环比下降0.2% 同比下降4%

2022-03-19

深圳技术进出口全年合同数量共1347项 同比增长2.51%

2022-03-19

邢台柏乡:打造羊肚菌产业示范带 引领村级集体经济发展

2022-03-19

胡金秋32分17板 浙江广厦男篮“双杀”稠州金租

2022-03-19

新疆北部有降雪 湖北、湖南等地出现大雾

2021-12-13

黑龙江新增本土核酸检测初筛阳性人员5例 均在讷河市

2021-12-13

“恋爱盲盒”抽的不是爱情,是急功近利的心

2021-12-13

北京12月12日新增2例境外输入确诊病例

2021-12-13

满洲里本轮疫情社会面“清零”,迎来拐点!

2021-12-13

北京今日晴冷在线气温低 本周中后期或再遭冷空气侵袭

2021-12-13

近六成受访大学生表示自己不能脱离表情包

2021-12-13

停车费上涨,昆明部分医院停车难缓解了吗?

2021-12-13

救人快递小哥:我不想成为网红 将继续踏实工作

2021-12-13

野猪拟从“三有”野生动物名录中除名

2021-12-13

“三有”名录删除野猪,要把握好捕杀与保护的度

2021-12-13

齐齐哈尔讷河发现5例核酸检测初筛阳性人员

2021-12-13

野猪退出“三有”名录不是一道“滥捕滥杀令”

2021-12-13

网红蹭“遗孤”流量是变相吃“血馒头”

2021-12-13

演员涂们病逝 曾被称为“草原王爷专业户”

2021-12-13

被偷走的那些年:被拐14年后他们成夹在中间的孩子

2021-12-13

文峰道歉:官微官网整改,10个工作日内对接预付卡备案

2021-12-13

山东烟台海域货船沉没已致9人遇难 搜救仍在进行

2021-12-13

浙江绍兴累计报告确诊病例107例 无症状感染者1例

2021-12-13

第三届国际白鹤论坛举办 中外专家聚焦生物多样性保护

2021-12-13

浙江绍兴本轮疫情已累计报告107例确诊病例

2021-12-13

“零容忍” 浙江宁波公安机关将严厉打击涉疫违法犯罪行为

2021-12-13

宁波镇海疫情未发生外溢 44例感染者均在蛟川街道管控区内

2021-12-13

来华留学生游梵净山 感知贵州多彩文化

2021-12-13

货船触礁进水 福建海警成功营救6名船员

2021-12-13

战“疫”时刻:浙江疫情下的“特殊”二三事

2021-12-13

浙江宁波44人已确诊 年龄最小为2岁 最大为70岁

2021-12-13

浙江宁波此轮疫情病毒为德尔塔变异株(AY.4 进化分支)华沙株

2021-12-13

南粤古驿道徒步体验活动(西京古道站)韶关举行

2021-12-13

上海试点驾驶证“学法减分” 一年最多减6分

2021-12-13

河南周口发布公告 明确过节确需返乡应提前3天报备

2021-12-13

浙江越城区新增1例确诊病例 系上虞区病例的密接者

2021-12-13

浙江已报告阳性感染者139例 明确严控跨省出行

2021-12-13

第二届鄱阳湖国际观鸟周在“中国候鸟小镇”江西吴城开幕

2021-12-13

“酥油茶‘遇见’咖啡,好比空气中飘着香水味”

2021-12-13

西安新增1例本土确诊病例 活动轨迹公布

2021-12-13

浙江三地病例感染病毒均属于德尔塔变异株

2021-12-13

山东烟台海域一载有14人货船沉没 9人已无生命体征

2021-12-13

浙江严控跨省出行 中高风险地区人员严格限制出行

2021-12-13

陕西西安一诊所工作人员确诊 8份核酸检测环境样本结果呈阳性

2021-12-13

浙江绍兴越城区在集中隔离点发现1名核酸检测阳性感染者

2021-12-13

截至12日15时 西安已追踪管控密切接触者486人

2021-12-13

陕西西安一诊所工作人员被诊断为确诊病例 活动轨迹公布

2021-12-13

抗疫特写:交通封控后的上虞“24小时”

2021-12-13

居家观察期间擅自离开封控区域 浙江上虞警方处罚3人

2021-12-13

杭州发布致市民公开信:非必要不流动 少聚集

2021-12-13

满洲里新冠肺炎救治感控督导组:“战事”越严峻 越要教会大家如何躲“子弹”

2021-12-13

浙江宁波、绍兴和杭州三地累计新冠肺炎确诊病例138例

2021-12-13

宁波镇海疫情下的“产科故事”:三天迎来28个宝宝

2021-12-13

内蒙古满洲里核酸检测累计检出阳性样本532份

2021-12-13

内蒙古满洲里新增确诊病例3例

2021-12-13