预计将于 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
标签:
最新推荐
预计将于2023年2月发布的Go1 20有一个小的变化,对于那些大量使用错误包装的应用程序来说,可能会有效...
广东四会泰隆村镇银行违法被罚大股东为泰隆商业银行2022年12月27日14:28中国经济网
SUPERJUNIOR在整个周末给观众带来了欢笑。SUPERJUNIOR出演了12月25日播出的SBS《我家的熊孩子》(音译),...
因为“阳了”而被迫停了一周时间未更新文章,现在总算是慢慢恢复过来,今天又可以与大家聊聊股市。今天...
何氏眼科(301103)12月26日在投资者关系平台上答复了投资者关心的问题。投资者:您好,网传某上市眼科集...
文 北京青年报记者 张恩杰2022年,73岁的梁晓声和88岁的王蒙宝刀未老,继续为青春讴歌。七旬梁晓声聚...
前央视主持人徐丛林曾主持过CCTV2《第一时间》等节目,从业十多年时间,经验丰富。12月23日,他在网络上...
1、产生罚息这是金钱上的损失。如果信用贷款出现逾期,贷款机构会先电话催收贷款,提醒借款人还款,同时...
京投所属轨道公司相关负责人介绍,16号线和昌平线南延两条线路均是北京城市南北向骨干线路。即将开通的1...
2019年4月26日,东莞检察机关以涉嫌集资诈骗罪、非法吸收公众存款罪依法批准逮捕东莞团贷网互联网科技服...
中新网广州12月23日电 (记者 郭军)记者23日从广铁集团获悉,12月2
银座股份:控股股东鲁商集团增持计划已完成累计增持520万股12月22日,银座集团股份有限公司发布公告称,...
上海东方华银律师事务所 关于金圆环保股份有限公司致:金圆环保股份有限公...
财达证券(600906)12月22日在投资者关系平台上答复了投资者关心的问题。投资者:近期河北证监局对贵公司...
目前智能手表产品中,体验比较完善的,应该就是AppleWatch了,当然它的价格也比较高。如今,苹果开始促...
PMP的考试成绩不是按分数来算的,也没有存在合格标准的说法,只有通过与不通过的情况。PMP考试考试一共...
餐饮股早盘涨幅居前,截至发稿,海底捞(06862 HK)涨3 75%,报19 94港元;奈雪的茶(02150 HK)涨3 59...
12月20日笔记本电脑板块较上一交易日下跌0 52%,诚迈科技领跌。当日上证指数报收于3073 77,下跌1 07...
智通财经APP获悉,据报道,伊朗外交部长兼首席核谈判代表周二在约旦会见了欧盟外交政策负责人和协调伊朗...
路德环境(688156)12月20日在投资者关系平台上答复了投资者关心的问题。投资者:请问公司是否了解五粮液...
好多问题呀,开始回答或者提问前,其实可以看看问题本身是不是有问题,像黄执中一样。------这个问题首...
中密控股(300470)12月19日在投资者关系平台上答复了投资者关心的问题。
同花顺数据中心显示,肇民科技12月16日获融资买入10 64万元,占当日买入金额的2 71%,当前融资余额590...
“我们都好都好,家里也都打理得很好,没有什么困难!”近日,扶沟县柴岗乡从河村监测户李小可满足地对...
2022年财富管理“年终盘点”
本文来源:时代商学院作者:黄锐孙一鸣来源|时代商学院作者|黄锐孙一鸣编辑|孙一鸣12月16日,祖名股份(...
学习摄影一段时间,很多人都会进入一个瓶颈期。这时如果我们不想办法突破,就很难提高。观察身边一些摄...
据国家邮政局测算,11月份中国快递发展指数同比增长1 2%;受“双11”网购促销带动,11月1日—11日快递...
合同管理包括这些基本要求:1、同一份合同,不同的岗位分管,基础信息须一致;2、各岗位监管的合同要素...
大河报·豫视频特派卡塔尔记者王玮皓卡塔尔世界杯前夕,中国给东道主送上了一份大礼——国宝大熊猫一对...
截至2022年12月15日收盘,贝肯能源(002828)报收于9 19元,下跌2 75%,换手率11 53%,成交量17 39万...
6500亿元!刚刚,央行发布重要公告
财信发展:关于持股5%以上股东股份减持计划的预披露公告
证券时报e公司讯,今日两市股指小幅高开后维持震荡整理态势。截至收盘,沪指涨0 01%,深证成指跌0 02%...
中国天气网消息今天(12月14日),全国大部降水仍稀少,雨雪零散分布,冷空气影响也进入尾声阶段。不过...
随着“双减”工作推进,校外培训治理日趋细化,学科类隐形变异培训成为亟待解决的难题。12月13日,教育...
12月13日盘中消息,9点44分*ST西发(000752)触及涨停板。目前价格5 0,上涨5 04%。其所属行业非白酒...
证券代码:600539 证券简称:狮头股份 公告编号:临2022-110 ...
日前,中国信通院发布《2022年三季度互联网投融资运行情况》报告,报告显示,2022年三季度,受到复杂严...
证券代码:002317 公告编号:2022-106 广东众生药业股份有限公司 本公...
A P C 寻求出售,估值约2亿欧元
2022年12月7日新华锦(600735)发布公告称公司于2022年12月7日召开业绩说明会。具体内容如下:问:请董...
证券代码:300461 证券简称:田中精机 公告编号:2022-100 ...
律师兼任调解员,不打官司也能化解纠纷,代理调解受指派的公益性案件还免费。这是兵团第十师北屯市探索...
根据教育部教育考试院统一安排,2022年上半年全国大学英语四、六级口语考试将于5月21日-22日举行,笔试...
学生代购的“苦”与“乐” “你问的这个产品现在做促销活动,买一件包邮,还送小样和面膜……”...
11月11日大连市新冠肺炎疫情防控总指挥部发布,11月10日0时至24时,大连市新增21例本土新冠肺炎确诊...
纤维素制成闪光材料无毒可降解 或彻底改变化妆品行业 科技日报北京11月11日电 (实习记者张...
海洋中或堆积了2 8万吨新冠废物 科技日报北京11月11日电 (记者刘霞)据美国趣味科学网站10日报...
开屏广告又现新花招,换个马甲就重来? ■ 来论 据媒体报道,“双十一”期间,一些App的开屏...
一张陌生人的图片“活化”成为视频? 深度合成正瓦解“眼见为实”
一张陌生人的图片“活化”成为视频? 深度合成正瓦解“眼见为实”
Copyright © 2015-2022 全球律师网版权所有 备案号:豫ICP备20009784号-11 联系邮箱:85 18 07 48 3@qq.com