天天亮点!GO 1.20 新功能:多重错误包装 - 行业动态 -

当前位置:首页  >  行业动态  > 正文

天天亮点!GO 1.20 新功能:多重错误包装

天天亮点!GO 1.20 新功能:多重错误包装
2022-12-29 16:43:34

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

标签: 错误类型 应用程序

(责任编辑:news01)
山西因灾停电用户95%以上已恢复供电

山西因灾停电用户95%以上已恢复供电

  新华社太原10月12日电(记者梁晓飞)记者12日从国网山西省电力公司了解到,受近日强降雨影响,山西运...
10-12 14:06:20
浙江仙都黄帝祭典弦歌不绝 慎终追远赓续中华根脉

浙江仙都黄帝祭典弦歌不绝 慎终追远赓续中华根脉

  中新网丽水10月12日电(记者 范宇斌)四海同心祭始祖,共同富裕启华章。辛丑(2021)年中国仙都祭祀轩...
10-12 14:06:20
教育部:校外培训机构登记为非营利前暂停招生收费

教育部:校外培训机构登记为非营利前暂停招生收费

  记者12日从教育部了解到,教育部校外教育培训监管司近日就校外培训机构“营改非”工作作出部署,要...
10-12 14:06:20
山西介休部分村庄启动灾后消杀防疫

山西介休部分村庄启动灾后消杀防疫

  中新网晋中10月12日电 (记者 陆祁国)随着抽排洪水作业进度加快,部分村庄积滞洪水水位明显下降。1...
10-12 14:06:20
中国新疆“河狸公主”:一百万中国青年救助保护河狸

中国新疆“河狸公主”:一百万中国青年救助保护河狸

  (生物多样性大会)中国新疆“河狸公主”:一百万中国青年救助保护河狸  中新社昆明10月12日电 题...
10-12 14:06:20
“母亲河畔的中国”网络主题活动在河南开封启动

“母亲河畔的中国”网络主题活动在河南开封启动

  中新网开封10月12日电 12日上午,“母亲河畔的中国”网络主题活动启动仪式在河南开封举行。  本...
10-12 14:06:20
陕煤彬长公司一煤矿发生局部冒顶事故 4人死亡

陕煤彬长公司一煤矿发生局部冒顶事故 4人死亡

  记者从陕西省咸阳市委宣传部获悉,10月11日13时50分许,陕煤彬长公司胡家河煤矿402104工作面发生局...
10-12 14:06:19
广东海警局联合广东省公安厅开展“清湾行动” 查获非法船舶860艘

广东海警局联合广东省公安厅开展“清湾行动” 查获非法船舶860艘

  中新网北京10月12日电 为坚决遏制珠江口水域走私活动多发态势,坚决打击违法犯罪分子嚣张气焰,全...
10-12 14:06:19
浙江长兴公安摧毁一盗掘古墓犯罪团伙 追回文物40余件

浙江长兴公安摧毁一盗掘古墓犯罪团伙 追回文物40余件

  中新网湖州10月12日电(施紫楠 吴俊杰)在小说和影视剧中,盗墓者常被称为“摸金校尉”,十分神秘。...
10-12 14:06:19
山西 陕西两地加紧防汛救灾

山西 陕西两地加紧防汛救灾

  央视网消息(新闻联播):山西、陕西两地加紧防汛救灾。今天(10月11日),财政部、应急管理部向两省紧...
10-12 14:06:19
强降雨致山西公路灾损6021.36公里

强降雨致山西公路灾损6021.36公里

  记者今天(10月12日)从山西省人民政府召开的新闻发布会上了解到,受连续强降雨影响,山西公路交通基...
10-12 14:06:19
哈尔滨多地风险等级调整为低风险

哈尔滨多地风险等级调整为低风险

  中新网哈尔滨10月12日电 (记者 刘锡菊)12日,哈尔滨市应对新冠肺炎疫情工作指挥部发布关于调整哈...
10-12 14:06:19
南京破获一起非法制作出租游戏账号案:未成年人5元租个账号就能打游戏

南京破获一起非法制作出租游戏账号案:未成年人5元租个账号就能打游戏

  中新网南京10月12日电 (记者 申冉)非法购买大量个人信息,与游戏账号进行绑定,制成经过“实名认...
10-12 14:06:18
10月2日至7日 山西出现有气象记录以来秋季最强降水过程

10月2日至7日 山西出现有气象记录以来秋季最强降水过程

  记者今天从山西省人民政府召开的新闻发布会上了解到,6月份入汛以来,受低涡切变线和西太平洋副热带...
10-12 14:06:18
台风“圆规”将于13日下午到夜间登陆海南岛

台风“圆规”将于13日下午到夜间登陆海南岛

  中新网海口10月12日电 (符晓虹 李凡 翁小芳)据海南省气象台12日介绍,今年第18号台风“圆规”(强...
10-12 14:06:18
哈尔滨11日新增治愈出院本土确诊病例15例

哈尔滨11日新增治愈出院本土确诊病例15例

  中新网哈尔滨10月12日电 (记者 刘锡菊)12日,哈尔滨市卫健委发布哈尔滨市10月11日0-24时疫情通报...
10-12 14:06:18
涉案金额756亿元!甘肃特大地下钱庄案终告破

涉案金额756亿元!甘肃特大地下钱庄案终告破

  外汇管理部门与公安机关日前在甘肃联合破获一起特大地下钱庄案件,涉案金额高达756亿元,沉重打击了...
10-12 14:06:17
强降雨致山西因灾死亡15人 直接经济损失50.29亿元

强降雨致山西因灾死亡15人 直接经济损失50.29亿元

  今天(10月12日)上午10时,山西省召开防汛救灾新闻发布会,通报相关情况。  记者今天从山西省人民...
10-12 14:06:17
强降雨致山西因灾死亡15人 直接经济损失50.29亿元

强降雨致山西因灾死亡15人 直接经济损失50.29亿元

  中新网10月12日电 10月12日,山西省召开防汛救灾新闻发布会,通报相关情况。据介绍,截至目前,此...
10-12 14:06:17
特写:养老院的78位老人:雨过天晴,我们回家

特写:养老院的78位老人:雨过天晴,我们回家

  中新网晋中10月12日电 题:养老院的78位老人:雨过天晴,我们回家  中新网记者 杨杰英  “洪...
10-12 14:06:17
新型诈骗套路难防“领导”喊你转账,转吗?

新型诈骗套路难防“领导”喊你转账,转吗?

  “领导”加你微信,关心你的工作和生活,还约你到他办公室安排工作?先别激动,这可能是一场网络骗...
10-12 14:06:17
10月以来山西发生地质灾害33起 地质灾害防治工作形势非常严峻

10月以来山西发生地质灾害33起 地质灾害防治工作形势非常严峻

  10月以来山西发生地质灾害33起 地质灾害防治工作形势非常严峻  记者今天(10月12日)从山西省人民...
10-12 14:06:17
付款不被骗、上网不留痕……这些“黑科技”现身2021年国家网络安全宣传周

付款不被骗、上网不留痕……这些“黑科技”现身2021年国家网络安全宣传周

  在反诈盲盒中体验如何保住自己的“钱袋子”,在现场观摩如何安全上网不留痕迹,大型油气企业如何远...
10-12 14:06:16
受台风“圆规”影响 福建多处景点关闭

受台风“圆规”影响 福建多处景点关闭

  受今年第18号台风“圆规”及冷空气共同影响,福建省附近海上风力逐渐加大,为确保安全,自2021年10...
10-12 14:06:16
浙江女子10天前看人杀鸭感染鹦鹉热:39℃高烧 乏力干咳

浙江女子10天前看人杀鸭感染鹦鹉热:39℃高烧 乏力干咳

  浙江女子突发39℃高烧,路都走不了!近期出现一人感染后身亡,医生紧急提醒……  “乏力,发热,...
10-12 14:06:16
95后姑娘坚持画云千余天 网友:温柔笔触里有浪漫诗意

95后姑娘坚持画云千余天 网友:温柔笔触里有浪漫诗意

  95后湖北姑娘坚持画云千余天   千万网友称赞:她温柔的笔触里有种浪漫的诗意  你喜欢看云吗?...
10-12 14:06:16
保险公司频遭“内鬼”撬单骗津贴 背后犯罪团伙落网

保险公司频遭“内鬼”撬单骗津贴 背后犯罪团伙落网

保险公司频遭“内鬼”撬单骗津贴,办案人员经深入调查牵出背后的犯罪团伙——  揭秘“保险黑产团伙”...
10-12 14:06:16
黑龙江省高风险地区清零

黑龙江省高风险地区清零

  中新网哈尔滨10月12日电 (程岩 记者 史轶夫)黑龙江省卫健委12日发布消息,11日0-24时,黑龙江省...
10-12 14:06:16
绑架案牵出医疗腐败窝案 彰显有线索一查到底反腐决心

绑架案牵出医疗腐败窝案 彰显有线索一查到底反腐决心

  绑架案牵出医疗腐败窝案的沉重警示  近日,广西来宾市纪委监委印发《来宾市以案促改警示教育读本...
10-12 14:06:15
河北平山一通勤班车发生涉水倾覆事故 公安部工作组赴现场指导工作

河北平山一通勤班车发生涉水倾覆事故 公安部工作组赴现场指导工作

  法治日报全媒体记者 董凡超  10月11日7时许,河北省石家庄市平山县钢城路滹沱河大桥施工辅路发生...
10-12 14:06:15

为您推荐

精彩推送