GO 1.20 新功能:多重错误包装
预计将于 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 新功能:多重错误包装
- 阳光天眼风险地图平台荣获“2022年度值得关注保险创新项目”
- 12月27日盛弘股份涨5.26%,泰达转型机遇股票A基金重仓该股
- 世界时讯:厚植绿色沃土 共享生态福利
- 环球通讯!优宁维(301166)1997.93万股限售股将于12月28日解禁上市,占总股本23.05%
- 全球即时看!招联好期贷借款逾期六年会上征信吗
- 天天信息:凌云光:截止2022年12月20日,公司股东17,121户
- 西兰花怎么凉拌?
- 互联网医院分流轻症挤兑 人手、运力需支援
- 最新消息:特斯拉股东不再相信马斯克?律师警告再卖就让他赔钱
- 东方创业: 关于东方国际创业股份有限公司2022年限制性股票激励计划预留股份授予事项之法律意见书-世界播资讯
- 安德利(605198)12月23日主力资金净卖出211.94万元 全球今日讯
- 未名医药(002581)12月22日主力资金净卖出1511.75万元
- 中石化、中海油近期人事变动_天天视点
- 世界简讯:镇洋发展(603213)12月22日主力资金净买入554.25万元
- 新冠导致嗅觉丧失关键原因找到:与嗅觉神经细胞持续受到免疫攻击有关
- 杭汽轮B: 关于调整2021年限制性股票激励计划回购数量和回购价格的公告
- 嘉宾金句|金融行业数字化转型的当下和未来_今日快讯
- 雷科防务“无人机防御雷达”荣获高交会“优秀产品奖” 以卓越产品性能助推多场景应用
- 唯赛勃(688718):上海唯赛勃环保科技股份有限公司2022年第二次临时股东大会决议|短讯
- 金禄电子董秘回复:2021年度公司境外营业收入为5.49亿元,占主营业务收入的比重在四成以上
- 12月19日基金净值:中银招利债券A最新净值1.0807,跌0.15%
- 卢伟冰又换了一台新手机:上一部是小米13 Pro
- 上海:将发热诊疗药品向社区倾斜 确保满足居民发热诊疗就近就医需求
- 【环球热闻】这个波卡钱包可以让你玩转 NFT、XCM、Staking 和 Crowdloan|SubWallet 钱包体验报告
- 盗窃五千块判多少年|世界独家
- 3年前靠卖惨骗钱,被揭穿后遭老公抛弃的水泥妹,如今怎样了?
- 金晨翻车了!冤不冤?
- 会稽山(601579):连续三个交易日收盘价格涨幅偏离值累计超过20%
- 创新饮水体验,九阳净水斩获2022中国厨卫创新峰会3项大奖 全球观焦点
- 离婚时工伤赔偿款怎么分割 全球独家
- 中盐化工: 中盐化工关于董事辞职的公告
- 视焦点讯!奥美医疗董秘回复:公司相关产品目前主要保供院线市场,线上零售渠道可能会存在无法购买的情况
- 世界快看点丨ST升达: 股票交易异常波动公告
- 焦点热门:宝利国际(300135)12月14日主力资金净卖出3688.35万元
- 世界快看:人民币兑美元中间价报6.9535,调升211个基点
- cfa考试中午考完几点放人?看完你就知道了!
- 今日报丨长沙出台“招商新政十二条” 最高奖励1000万元
- 全球快播:网贷逾期85万会有严重后果吗
- 柏楚电子(688188.SH):拟使用募集资金向2家子公司提供借款、以实施“智能切割头扩产项目”及“超高精密驱控一体研发项目”
- 莱茵生物董秘回复:公司会考虑一些类似的投资项目和合作,未来如有相关计划公司将按相关要求进行披露
- 港股异动丨康希诺AH均涨近6% 广州昨日启动吸入式新冠疫苗接种
- * 高管拟减持不超36.51万股,圣农发展盘中跌超6%
- 宋城演艺(300144)12月6日主力资金净买入3281.22万元
- 多家航司上调国内航线燃油附加费收费标准 降低用户暑期旅游的出行成本
- 瓶装水的走心之旅 瓶装水应道如何做到走心?
- 加快核心技术突破 我国气象服务业将于2025年迈向新的台阶
- 中宣部:斩断伸向未成年人的盗版侵权“魔爪”
- 国务院开展根治欠薪冬季专项行动 为农民工追讨欠薪68.62亿元
- 坚决打击网络暴力 帮助网民切实维护自身权益
- 七台河市鹿山煤矿二井发生矿震 4人被困井下
- 暂停27天后 福建仙游火车站重启
- “大工匠”传经送宝
- 黑龙江七台河一煤矿发生矿震 目前井下4人被困
- 湖南临澧凌晨拉响防空警报 回应:短路引起故障
- 广东国庆假期高速公路车流总量超5000万车次 大湾区车流量同比增长10%
- 南海热带低压加强为台风 海南发布台风三级预警
- 广东升级广交会期间疫情防控举措 所有持证人员核酸检测“应检尽检”
- 国庆假期北京市接待旅游总人数861.1万人次
- 高福、童贻刚团队:新冠病毒溯源将是一场“持久战”
- 四川水利国庆假期全力应战最强秋汛
- 南海热带低压将于8日夜间至9日上午在海南岛东部登陆
- 聊天群背后的黑色产业链 金钱诱惑下被害人变成害人者
- 黑龙江省绥化市全域均为低风险地区
- 走近网瘾少年们:他们沉迷网络的病根何在?
- 节后第一天北京白天晴或多云利于出行 夜间起秋雨或再上线
- 走访抗美援朝纪念馆:长津湖的寒冷,与战斗一样残酷
- 绥化全域低风险!黑龙江绥化北林区一地调整为低风险
- 农业农村部:确保秋粮丰收到手、明年夏季粮油播种
- 中国故事丨“沉浸式”盘点今年的教育好声音!
- 升旗、巡岛、护航标、写日志,他们一生守护一座岛
- 他从一窍不通的“门外汉”,到重装空投“兵专家”
- 获2021年诺奖的蛋白,结构由中国学者率先解析
- “双减”后首个长假:亲子游、研学游需求集中释放
- 天山脚下,触摸丝路发展新脉动
- 且看新疆展新颜
- 《山海情》里“凌教授”的巨菌草丰收啦
- “双减”出台两个月,组合拳如何直击减负难点?
- IP类城市缘何吸引力强?玩法创新带动游客年轻化
- 面对婚姻,“互联网世代”的年轻人在忧虑什么?
- 沙害是自然界的恶魔,而他是荒沙碱滩的征服者
- “辱华车贴”商家及客服被行拘,处罚要不放过每一环
- 网游新政下,未成年人防沉迷的“主战场”在哪?
- 160万骑手疑似“被个体户”?平台不能当甩手掌柜
- 报告显示:这个国庆假期,粤川浙桂赣旅游热度最高
- 陈毅元帅长子忆父亲叮嘱:你们自己学习要好,就可以做很多事儿
- 北京国庆7天接待游客超861万人次 冬奥线路受青睐
- 从1.3万元降到700元,起诉书揭秘心脏支架“玄机”
- 都市小资还是潮流乐享?花草茶市场呈爆发性增长
- 国庆主题花坛持续展摆至重阳节
- 云南保山:170公里边境线,4000余人日夜值守
- 线上教学模式被盯上,网络付费刷课形成灰色产业链
- 全国模范法官周淑琴:为乡村群众点燃法治明灯
- 嘉陵江出现有记录以来最强秋汛
- 中国科技人才大数据:广东总量第一,“北上”这类人才多
- 神经科学“罗塞塔石碑”来了:迄今为止最完整的大脑细胞图谱
- 多地网友投诉遭遇旅游消费骗局,呼吁有关部门严查乱象
- 受南海热带低压影响 海南海口三港预计停运将持续到10日白天
- 农业农村部:确保秋粮丰收到手、明年夏季粮油播种
- 广州10月8日至20日对所有从省外来(返)穗人员实施核酸检测
X 关闭
行业规章
X 关闭