91版本福利群_精品国产丝袜自在线拍国_诱人的女老板HD在线观看_国产一卡2卡3卡4卡高清_亚洲AV无码国产精品色无友情

分享縮略圖
首頁(yè) 資訊 企業(yè) 供應(yīng)商 熱門(mén)產(chǎn)品 供求信息 閥門(mén)品牌 產(chǎn)品分類 企業(yè)展示 關(guān)注

GO 1.20 新功能:多重錯(cuò)誤包裝

發(fā)布時(shí)間:2022-12-29 10:27:16  |  來(lái)源:  |  

預(yù)計(jì)將于 2023 年 2 月發(fā)布的 Go 1.20 有一個(gè)小的變化,對(duì)于那些大量使用錯(cuò)誤包裝的應(yīng)用程序來(lái)說(shuō),可能會(huì)有效改進(jìn)它們的錯(cuò)誤處理方法。

讓我們看一下它的用法,但首先,需要簡(jiǎn)要回顧一下什么是錯(cuò)誤包裝。如果你已經(jīng)掌握了可以直接跳到下面的 “Go 1.20 新功能” 部分以獲取新的信息。


(資料圖片)

Go 中的錯(cuò)誤是實(shí)現(xiàn)一個(gè)非常簡(jiǎn)單的接口:

typeerrorinterface{Error()string}

錯(cuò)誤類型可以是任何東西,從string本身到int,但通常它們是struct類型。下面這個(gè)例子來(lái)自標(biāo)準(zhǔn)庫(kù):

typeerrstruct{sstring}func(e*err)Error()string{returne.s}

要檢查 Go 中的錯(cuò)誤,你只需比較一個(gè)值(在本例中為int值):

iferr==io.EOF{//...}

第二種常見(jiàn)的用法是檢查錯(cuò)誤的類型,那也意味著要寫(xiě)更多的代碼:

ifnerr,ok:=err.(net.Error){//...(usenerrwhichisanet.Error)}

在上面的例子中,類型斷言測(cè)試類型net.Error的err值,并創(chuàng)建一個(gè)新變量nerr,它可以在 if 語(yǔ)句中使用。Go 中的錯(cuò)誤方便理解、易于使用且非常高效。

錯(cuò)誤包裝

從 Go 1.13 開(kāi)始,引入了錯(cuò)誤包裝。包裝允許將錯(cuò)誤嵌入到其他錯(cuò)誤中,就像在其他語(yǔ)言中包裝異常一樣。這非常實(shí)用,比如函數(shù)遇到 “record not found” 錯(cuò)誤時(shí),可以向錯(cuò)誤信息中添加更多上下文信息,例如 “unknown user: record not found”。

Go 中錯(cuò)誤包裝設(shè)計(jì)背后的有趣想法是:契約不用關(guān)心錯(cuò)誤類型、結(jié)構(gòu)或它們是如何創(chuàng)建的。而唯一關(guān)注的是解包過(guò)程和轉(zhuǎn)換為字符串,因?yàn)檫@兩者是必須的。這就非常容易實(shí)現(xiàn):支持解包的錯(cuò)誤類型必須實(shí)現(xiàn)Unwrap() error方法。

標(biāo)準(zhǔn)庫(kù)中沒(méi)有(命名的)接口可以向您展示,因?yàn)榻涌谑请[式實(shí)現(xiàn)的,沒(méi)有必要單獨(dú)寫(xiě)一個(gè)。這里我們寫(xiě)一個(gè)只是為了更好說(shuō)明這篇文章:

typeWrappedErrorinterface{Unwrap()error}

我們來(lái)看看 Go 標(biāo)準(zhǔn)庫(kù)(實(shí)際上是 package fmt)中是如何實(shí)現(xiàn)包裝錯(cuò)誤的:

typewrapErrorstruct{msgstringerrerror}func(e*wrapError)Error()string{returne.msg}func(e*wrapError)Unwrap()error{returne.err}

由于上面錯(cuò)誤類型實(shí)現(xiàn)了Error() string方法,所以說(shuō) Go 中的錯(cuò)誤實(shí)際上最終是字符串并沒(méi)有錯(cuò),因此需要一種創(chuàng)建這些字符串的良好機(jī)制。這就是標(biāo)準(zhǔn)庫(kù)中的函數(shù)fmt.Errorf發(fā)揮作用的地方:

varRecordNotFoundErr=errors.New("notfound")constname,id="lzap",13werr:=fmt.Errorf("unknownuser%q(id%d):%w",name,id,recordNotFoundErr)fmt.Println(werr.Error())

一個(gè)特殊格式的動(dòng)詞%w,每次調(diào)用只能使用一次(稍后會(huì)詳細(xì)介紹),用于錯(cuò)誤參數(shù)。除此之外,該函數(shù)的工作方式類似于fmt.Printf函數(shù)。下面的例子打印了這個(gè)結(jié)果:

unknownuser"lzap"(id13):notfound

如你所見(jiàn),錯(cuò)誤包裝本質(zhì)上是一個(gè)鏈表。要解包錯(cuò)誤,請(qǐng)使用errors.Unwrap函數(shù),該函數(shù)將為鏈表中的最后一個(gè)錯(cuò)誤值返回nil。要檢查錯(cuò)誤類型或值,需要遍歷整個(gè)列表,這對(duì)于需要進(jìn)行頻繁的錯(cuò)誤檢查不太實(shí)用。幸運(yùn)的是,有兩個(gè)輔助函數(shù)可以做到這一點(diǎn)。

檢查包裝錯(cuò)誤列表中的值:

iferrors.Is(err,RecordNotFoundErr){//...}

檢查特定類型(下面例子是來(lái)自標(biāo)準(zhǔn)庫(kù)的網(wǎng)絡(luò)錯(cuò)誤):

varnerr*net.Erroriferrors.As(err,&nerr){//...(usenerrwhichisa*net.Error)}

以上總結(jié)了 Go 1.13 及更高版本中的錯(cuò)誤包裝。

Go 1.20 新特性

讓我們看看 Go 1.20 中真正的新功能,從函數(shù)errors.Join開(kāi)始,它通過(guò)可變參數(shù)包裝錯(cuò)誤列表:

err1:=errors.New("err1")err2:=errors.New("err2")err:=errors.Join(err1,err2)fmt.Println(err)

當(dāng)事先不知道錯(cuò)誤數(shù)量時(shí),此功能可用于將錯(cuò)誤連接在一起。一個(gè)很好的例子是從 goroutines 收集錯(cuò)誤。值得一提的是,該函數(shù)將列表中的錯(cuò)誤與換行符連接起來(lái)。上面的代碼片段打?。?/p>

err1err2

對(duì)于許多應(yīng)用程序或(日志記錄)庫(kù)來(lái)說(shuō),這可能會(huì)存在問(wèn)題,它們期望錯(cuò)誤通常只是沒(méi)有換行符的字符串。幸運(yùn)的是,Go 1.20 中的另一個(gè)變化改變了fmt.Errorf的行為:該函數(shù)現(xiàn)在接受多個(gè)%w格式說(shuō)明符:

err1:=errors.New("err1")err2:=errors.New("err2")err:=fmt.Errorf("%w+%w",err1,err2)fmt.Println(err)

以前會(huì)導(dǎo)致格式錯(cuò)誤的格式字符串現(xiàn)在可以正確打?。?/p>

err1+err2

同時(shí)包裝多個(gè)錯(cuò)誤實(shí)現(xiàn)Unwrap() error,這是可能的嗎?

事實(shí)證明,在 Go 1.20 標(biāo)準(zhǔn)庫(kù)中有一種新的機(jī)制: 實(shí)現(xiàn)Unwrap() []error函數(shù)的錯(cuò)誤類型可以包裝多個(gè)錯(cuò)誤。讓我們來(lái)看看這是如何在庫(kù)中實(shí)現(xiàn)的:

typejoinErrorstruct{errs[]error}func(e*joinError)Error()string{//concatenateerrorswithanewlinecharacter}func(e*joinError)Unwrap()[]error{returne.errs}

一個(gè)理論上的接口,但標(biāo)準(zhǔn)庫(kù)中實(shí)際不存在,如下所示:

typeMultiWrappedErrorinterface{Unwrap()[]error}

由于 Go 不允許方法重載,因此每種類型都可以實(shí)現(xiàn)Unwrap() error或Unwrap() []error,但不能同時(shí)實(shí)現(xiàn)。還記得我提到過(guò)包裝錯(cuò)誤本質(zhì)上是一個(gè)鏈表嗎?實(shí)現(xiàn)前一個(gè)(新引入的)方法的類型實(shí)際上形成了一個(gè)鏈接樹(shù),函數(shù)errors.Is和errors.As的工作方式相同,只是現(xiàn)在它們需要遍歷樹(shù)而不是列表。根據(jù)文檔,該實(shí)現(xiàn)執(zhí)行預(yù)排序、深度優(yōu)先遍歷。

這確實(shí)是 Go 1.20 帶來(lái)的全部,它可能看起來(lái)是一個(gè)小的變化,但它提供了如何有效和干凈地處理錯(cuò)誤的新方法。在展示真實(shí)示例之前,讓我總結(jié)一下新功能:

新的Unwrap []error函數(shù)契約允許遍歷錯(cuò)誤樹(shù)。

新的errors.Join函數(shù),這是一個(gè)方便的函數(shù),用于連接兩個(gè)錯(cuò)誤字符串值(使用換行符)。

現(xiàn)有函數(shù)errors.Is和errors.As已更新,可以同時(shí)處理錯(cuò)誤列表和錯(cuò)誤樹(shù)。

現(xiàn)有函數(shù)fmt.Errorf現(xiàn)在接受多個(gè)%w格式動(dòng)詞。實(shí)踐

上面這一切都很棒,但是你如何在實(shí)踐中利用它呢?

在一個(gè)小型 REST API 微服務(wù)中,我們通過(guò)errors.New和fmt.Errorf處理來(lái)自 DAO 包(數(shù)據(jù)庫(kù))、REST 客戶端(其他后端服務(wù))和其他包的各種錯(cuò)誤。返回的 HTTP 狀態(tài)代碼應(yīng)該是 2xx、4xx 或 5xx,具體取決于錯(cuò)誤狀態(tài)以遵循最佳 REST API 實(shí)踐。實(shí)現(xiàn)此過(guò)程的一種方法是解開(kāi)主 HTTP 處理程序中的錯(cuò)誤并找出它是哪種錯(cuò)誤。

然而,通過(guò)多重錯(cuò)誤包裝,現(xiàn)在可以包裝根本原因(例如數(shù)據(jù)庫(kù)返回 “no records found” )和返回給用戶 HTTP 代碼(在本例中為 404)。

一個(gè)工作示例如下所示:

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)}

這將打?。?/p>

Willreturn404usernotfound:DB:recordnotfound(404)Willreturn401unauthorizedtocallotherservice:HTTPclient:unauthorized(401)

從這樣的人工代碼片段中可能看起來(lái)不太明顯的是,實(shí)際上的錯(cuò)誤聲明通常分布在許多包中,并且不容易跟蹤所有可能的錯(cuò)誤以確保所需的 HTTP 狀態(tài)代碼。在這種方法中,所有在一個(gè)地方聲明的應(yīng)用程序級(jí)包裝錯(cuò)誤也包含了 HTTP 代碼。

請(qǐng)注意,這在 Go 1.19 或更早版本中是不可能的,因?yàn)閒mt.Errorf函數(shù)只會(huì)包裝第一個(gè)錯(cuò)誤。該代碼確實(shí)在 1.19 上可以編譯,甚至不會(huì)產(chǎn)生運(yùn)行時(shí)恐慌,但它實(shí)際上不會(huì)工作。

顯然,常見(jiàn)的 HTTP 狀態(tài)代碼很容易成為一種新的錯(cuò)誤類型(基于int類型),因此可以通過(guò)errors.As輕松提取實(shí)際代碼,但我想讓示例保持簡(jiǎn)單。

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 上自由運(yùn)行上述代碼。確保使用 “dev branch” 或 Go 的 1.20+ 版本?,F(xiàn)有應(yīng)用

在你的應(yīng)用程序中實(shí)施新功能時(shí),請(qǐng)注意errors.Unwrap函數(shù)。對(duì)于具有Unwrap() []error的錯(cuò)誤類型,它總是返回nil:

err1:=errors.New("err1")err2:=errors.New("err2")err:=errors.Join(err1,err2)unwrapped:=errors.Unwrap(err)fmt.Println(unwrapped)

由于 Go 1.X 兼容性承諾,這會(huì)打印出 “nil”。當(dāng)你引入多個(gè)包裝錯(cuò)誤時(shí),請(qǐng)確保檢查展開(kāi)代碼。幸運(yùn)的是,典型 Go 代碼中的大部分錯(cuò)誤檢查都是使用errors.Is和errors.As完成的。

錯(cuò)誤包裝并不是 Go 中所有錯(cuò)誤處理的最終解決方案。它只是提供了一種干凈的方法來(lái)處理典型 Go 應(yīng)用程序中的錯(cuò)誤,對(duì)于簡(jiǎn)單應(yīng)用程序來(lái)說(shuō)也許就完全足夠了。原文地址:https://lukas.zapletalovi.com/posts/2022/wrapping-multiple-errors/原文作者:Luká? Zapletal本文永久鏈接:https://github.com/gocn/translator/blob/master/2022/w50_Wrapping_multiple_errors譯者:haoheipi

校對(duì):watermelo

往期推薦

谷歌發(fā)布查找開(kāi)源漏洞的Go工具OSV-Scanner

最好的Go框架:沒(méi)有框架?

「每周譯Go」如何在Go中構(gòu)造For 循環(huán)

想要了解Go更多內(nèi)容,歡迎掃描下方關(guān)注公眾號(hào),回復(fù)關(guān)鍵詞 [實(shí)戰(zhàn)群],就有機(jī)會(huì)進(jìn)群和我們進(jìn)行交流

分享、在看與點(diǎn)贊Go


關(guān)鍵詞:

 

推薦內(nèi)容

資訊

熱門(mén)產(chǎn)品

閥門(mén)品牌