欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語(yǔ)言中錯(cuò)誤處理的方式總結(jié)

 更新時(shí)間:2023年07月05日 10:35:54   作者:242030  
這篇文章會(huì)結(jié)合?errors?中的函數(shù),來(lái)討論一下?Go?中常見(jiàn)的?error?使用方式,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以了解一下

Go 的 error 有兩個(gè)很重要的特性:

  • error 就是一個(gè)普通的值,處理起來(lái)沒(méi)有額外的開(kāi)銷(xiāo)
  • error 的擴(kuò)展性很不錯(cuò),可以按照不同的場(chǎng)景來(lái)自定義錯(cuò)誤

在 Go1.13 之后,在 errors 包中提供了一些函數(shù),讓錯(cuò)誤的處理和追蹤更加方便一些。

這篇文章會(huì)結(jié)合 errors 中的函數(shù),來(lái)討論一下 Go 中常見(jiàn)的 error 使用方式。

這里說(shuō)的 errors 包是指 Go 中的原生 errors 包。

1、原生 error

在 Go 的錯(cuò)誤處理中,下面的代碼占絕大多數(shù):

if err != nil {
   return err
}

在滿(mǎn)足業(yè)務(wù)需求的情況下,這種錯(cuò)誤處理其實(shí)是最推薦的方式,這種直接透?jìng)鞯姆绞阶尨a之間的耦合度更低。在很多情況下,如果不關(guān)心錯(cuò)誤中的具體信息,使用這種方式就可以了。

2、提前定義好 error

原生的 error 在有些情況下使用起來(lái)就不是很方便,比如我需要獲得具體的錯(cuò)誤信息,如果還用上面的方式來(lái)使用error,可能會(huì)出現(xiàn)下面的代碼:

if err != nil && err.Error() == "invalid param" {
}

寫(xiě)過(guò)代碼的都知道上面的代碼很不優(yōu)雅,另外如果錯(cuò)誤的信息變化之后,這里的代碼邏輯就會(huì)出錯(cuò),可以通過(guò)把錯(cuò)誤定義成一個(gè)變量:

var (
    ErrInvalidParam = errors.New("invalid param")
)

那么上面的代碼就可以變成這樣:

if err != nil && err == ErrInvalidParam {
}

如果一次性需要處理的錯(cuò)誤比較多,還可以使用 switch 進(jìn)行處理:

if err != nil {
    switch err {
    case ErrInvalidParam:
        return
    case ErrNetWork:
        return
    case ErrFileNotExist:
        return
    default:
        return
    }
}

但是這種方式還不完美,因?yàn)?error 在傳遞的過(guò)程中,有可能會(huì)被包裝,以攜帶更多的堆棧信息,比如下面這樣:

if err != nil {
    // 在包裝錯(cuò)誤的時(shí)候,這里格式化錯(cuò)誤要使用%w
    return fmt.Errorf("add error info: %+v, origin error: %w", "other info", err)
}

假設(shè)上面被包裝的錯(cuò)誤是 ErrInvalidParam,那么在調(diào)用的地方判斷錯(cuò)誤,就不能使用下面的代碼:

if err != nil && err == ErrInvalidParam {
}

為了解決這個(gè)問(wèn)題, errors.Is 函數(shù)可以判斷被包裝的 error 中是否有預(yù)期的 error:

if errors.Is(err, ErrInvalidParam) {
}

盡量使用 errors.Is 來(lái)替代對(duì) error 的比較。

3、使用自定義的錯(cuò)誤類(lèi)型

上面的 error 使用方式在某些情況下還是不能滿(mǎn)足要求。假如對(duì)于上面的無(wú)效參數(shù) error,業(yè)務(wù)方想要知道具體是哪個(gè)參數(shù)無(wú)效,直接定義的錯(cuò)誤就無(wú)法滿(mǎn)足要求。error 本質(zhì)是一個(gè)接口,也就是是說(shuō),只要實(shí)現(xiàn)了 Error 方法,就是一個(gè) error 類(lèi)型:

type error interface {
    Error() string
}

那么就可以自定義一種錯(cuò)誤類(lèi)型:

type ErrInvalidParam struct {
    ParamName  string
    ParamValue string
}
func (e *ErrInvalidParam) Error() string {
    return fmt.Sprintf("invalid param: %+v, value: %+v", e.ParamName, e.ParamValue)
}

然后就可以使用類(lèi)型斷言機(jī)制或者類(lèi)型選擇機(jī)制,來(lái)對(duì)不同類(lèi)型的錯(cuò)誤進(jìn)行處理:

e, ok := err.(*ErrInvalidParam)
if ok && e != nil {
}

同樣可以在 switch 中使用:

if err != nil {
    switch err.(type) {
    case *ErrInvalidParam:
        return
    default:
        return
    }
}

在這里 error 同樣會(huì)存在被包裝的問(wèn)題,而 errors.As 剛好可以用來(lái)解決這個(gè)問(wèn)題,可以判斷出被包裝的錯(cuò)誤中是否存在某個(gè) error 類(lèi)型:

var e *ErrInvalidParam
if errors.As(err, &e) {
}

4、更靈活的 error 類(lèi)型

上面的方式已經(jīng)可以解決大部分場(chǎng)景的 error 處理了,但是在一些復(fù)雜的情況下,可能需要從錯(cuò)誤中獲取更多的信息,還包含一定的邏輯處理。

在 Go 的 net 包中,有這樣的一個(gè)接口:

type Error interface {
    error
    Timeout() bool  
    Temporary() bool
}

在這個(gè)接口中,有兩個(gè)方法,這兩個(gè)方法會(huì)對(duì)這個(gè)錯(cuò)誤類(lèi)型進(jìn)行處理,判斷是超時(shí)錯(cuò)誤還是臨時(shí)錯(cuò)誤,實(shí)現(xiàn)了這個(gè)接口的 error 要實(shí)現(xiàn)這兩個(gè) 方法,實(shí)現(xiàn)具體的判斷邏輯。

在處理具體 error 時(shí),會(huì)調(diào)用相應(yīng)的方法來(lái)判斷:

if ne, ok := e.(net.Error); ok && ne.Temporary() { 
     // 對(duì)臨時(shí)錯(cuò)誤進(jìn)行處理 
}
if ne, ok := e.(net.Error); ok && ne.Timeout() { 
     // 對(duì)超時(shí)錯(cuò)誤進(jìn)行處理 
}

這種類(lèi)型的 error 相對(duì)來(lái)說(shuō),使用的會(huì)比較少,一般情況下,盡量不要使用這么復(fù)雜的處理方式。

5、errors 中的其他能力

在 errors 包中,除了上面提到的 errors.Is 和 errors.As 兩個(gè)很有用的函數(shù)之外,還有一個(gè)比較實(shí)用的函數(shù)errors.Unwrap,這個(gè)函數(shù)可以從包裝的錯(cuò)誤中將原錯(cuò)誤解析出來(lái)。

可以使用 fmt.Errorf 來(lái)包裝 error,需要使用 %w 的格式化:

return fmt.Errorf("add error info: %+v, origin error: %w", "other info", err)

在后續(xù)的 error 處理時(shí),可以調(diào)用 errors.Unwrap 函數(shù)來(lái)獲得被包裝前的 error:

err = errors.Unwrap(err)
fmt.Printf("origin error: %+v\n", err)
package main
import (
	"errors"
	"fmt"
)
func main(){
	err1 := errors.New("zero")
	fmt.Printf("origin error: %+v\n", err1)
	err2 := fmt.Errorf("add error info: %+v, origin error: %w", "other info", err1)
	fmt.Printf("origin error: %+v\n", err2)
	err3 := errors.Unwrap(err2)
	fmt.Printf("origin error: %+v\n", err3)
}

程序輸出

origin error: zero
origin error: add error info: other info, origin error: zero
origin error: zero

6、注意

如果需要使用 goroutine 時(shí),應(yīng)該使用統(tǒng)一的 Go 函數(shù)進(jìn)行創(chuàng)建,這個(gè)函數(shù)中會(huì)進(jìn)行 recover ,避免因?yàn)橐吧鷊oroutine panic 導(dǎo)致主進(jìn)程退出。

func Go(f func()){
    go func(){
        defer func(){
            if err := recover(); err != nil {
                log.Printf("panic: %+v", err)
            }
        }()
        f()
    }()
}

在應(yīng)用程序中出現(xiàn)錯(cuò)誤時(shí),使用 errors.New 或者 errors.Errorf 返回錯(cuò)誤:

errors.Errorf("用戶(hù)余額不足, uid: %d, money: %d", uid, money)

如果是調(diào)用應(yīng)用程序的其他函數(shù)出現(xiàn)錯(cuò)誤,請(qǐng)直接返回,如果需要攜帶信息,請(qǐng)使用 errors.WithMessage。

// https://github.com/pkg/errors
errors.WithMessage(err, "其他附加信息")

如果是調(diào)用其他庫(kù)(標(biāo)準(zhǔn)庫(kù)、企業(yè)公共庫(kù)、開(kāi)源第三方庫(kù)等)獲取到錯(cuò)誤時(shí),請(qǐng)使用 errors.Wrap 添加堆棧信息。

切記,不要每個(gè)地方都是用 errors.Wrap 只需要在錯(cuò)誤第一次出現(xiàn)時(shí)進(jìn)行 errors.Wrap 即可。

根據(jù)場(chǎng)景進(jìn)行判斷是否需要將其他庫(kù)的原始錯(cuò)誤吞掉,例如可以把 repository 層的數(shù)據(jù)庫(kù)相關(guān)錯(cuò)誤吞掉,返回業(yè)務(wù)錯(cuò)誤碼,避免后續(xù)我們分割微服務(wù)或者更換 ORM 庫(kù)時(shí)需要去修改上層代碼。

注意我們?cè)诨A(chǔ)庫(kù),在大量引入的第三方庫(kù)編寫(xiě)時(shí)一般不使用 errors.Wrap 避免堆棧信息重復(fù)。

func f() error {
    err := json.Unmashal(&a, data)
    if err != nil {
        return errors.Wrap(err, "其他附加信息")
    }
    // 其他邏輯
    return nil
}

禁止每個(gè)出錯(cuò)的地方都打日志,只需要在進(jìn)程的最開(kāi)始的地方使用 %+v 進(jìn)行統(tǒng)一打印,例如 http/rpc 服務(wù)的中間件。

錯(cuò)誤判斷使用 errors.Is 進(jìn)行比較:

func f() error {
    err := A()
    if errors.Is(err, io.EOF){
        return nil
    }
    // 其他邏輯
    return nil
}

錯(cuò)誤類(lèi)型判斷,使用 errors.As 進(jìn)行賦值:

func f() error {
    err := A()
    var errA errorA
    if errors.As(err, &errA){
    }
    // 其他邏輯
    return nil
}

以上就是Go語(yǔ)言中錯(cuò)誤處理的方式總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Go錯(cuò)誤處理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • GO語(yǔ)言支付寶沙箱對(duì)接的實(shí)現(xiàn)

    GO語(yǔ)言支付寶沙箱對(duì)接的實(shí)現(xiàn)

    本文介紹了如何使用GO語(yǔ)言對(duì)接支付寶沙箱環(huán)境,包括秘鑰生成、SDK安裝和代碼實(shí)現(xiàn)等步驟,詳細(xì)內(nèi)容涵蓋了從秘鑰生成到前端代碼的每個(gè)階段,為開(kāi)發(fā)者提供了一條清晰的指引
    2024-09-09
  • Go 計(jì)時(shí)器使用示例全面講解

    Go 計(jì)時(shí)器使用示例全面講解

    這篇文章主要為大家介紹了Go 計(jì)時(shí)器使用示例全面講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • 3個(gè)Go語(yǔ)言中實(shí)用重構(gòu)技術(shù)分享

    3個(gè)Go語(yǔ)言中實(shí)用重構(gòu)技術(shù)分享

    代碼重構(gòu)是在不改變外部功能的情況下對(duì)現(xiàn)有代碼進(jìn)行改進(jìn),是編程的核心部分之一,本文為大家介紹了Go語(yǔ)言中3個(gè)實(shí)用重構(gòu)技術(shù),需要的可以參考一下
    2023-06-06
  • Go 標(biāo)準(zhǔn)庫(kù)增加metrics指標(biāo)探討分析

    Go 標(biāo)準(zhǔn)庫(kù)增加metrics指標(biāo)探討分析

    go中有一個(gè)神奇的標(biāo)準(zhǔn)庫(kù) runtime/metrics,提供了一系列預(yù)定義好的 Go 自身的相關(guān)指標(biāo),如果沒(méi)有編寫(xiě)過(guò)基礎(chǔ)監(jiān)控庫(kù)或者關(guān)注的比較少的朋友可能會(huì)沒(méi)接觸到這類(lèi)指標(biāo),本文展開(kāi)現(xiàn)有metrics 指標(biāo),并結(jié)合現(xiàn)有的社區(qū)討論一起看看還有沒(méi)有必要增加更多的標(biāo)準(zhǔn)庫(kù)指標(biāo)
    2023-10-10
  • Go語(yǔ)言中json序列化優(yōu)化技巧

    Go語(yǔ)言中json序列化優(yōu)化技巧

    在Go語(yǔ)言開(kāi)發(fā)中,JSON因其簡(jiǎn)潔和廣泛的兼容性,通常被用作數(shù)據(jù)交換的主要序列化格式,本文將探討JSON希望對(duì)大家有所幫助序列化的一些局限性,也算是一個(gè)小坑吧,并給出一些常用的解決方案,
    2024-11-11
  • Gorm存在時(shí)更新,不存在時(shí)創(chuàng)建的問(wèn)題

    Gorm存在時(shí)更新,不存在時(shí)創(chuàng)建的問(wèn)題

    這篇文章主要介紹了Gorm存在時(shí)更新,不存在時(shí)創(chuàng)建的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Go語(yǔ)句與表達(dá)式案例手冊(cè)深度解析

    Go語(yǔ)句與表達(dá)式案例手冊(cè)深度解析

    這篇文章主要為大家介紹了Go語(yǔ)句與表達(dá)式案例手冊(cè)深度解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Go語(yǔ)言中defer語(yǔ)句的用法

    Go語(yǔ)言中defer語(yǔ)句的用法

    這篇文章介紹了Go語(yǔ)言中defer語(yǔ)句的用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • 詳解Golang?ProtoBuf的基本語(yǔ)法總結(jié)

    詳解Golang?ProtoBuf的基本語(yǔ)法總結(jié)

    最近項(xiàng)目是采用微服務(wù)架構(gòu)開(kāi)發(fā)的,各服務(wù)之間通過(guò)gPRC調(diào)用,基于ProtoBuf序列化協(xié)議進(jìn)行數(shù)據(jù)通信,因此接觸學(xué)習(xí)了Protobuf,本文會(huì)對(duì)Protobuf的語(yǔ)法做下總結(jié),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助
    2022-10-10
  • Go Java算法最大單詞長(zhǎng)度乘積示例詳解

    Go Java算法最大單詞長(zhǎng)度乘積示例詳解

    這篇文章主要為大家介紹了Go Java算法最大單詞長(zhǎng)度乘積示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08

最新評(píng)論