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

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

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

Go 的 error 有兩個很重要的特性:

  • error 就是一個普通的值,處理起來沒有額外的開銷
  • error 的擴展性很不錯,可以按照不同的場景來自定義錯誤

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

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

這里說的 errors 包是指 Go 中的原生 errors 包。

1、原生 error

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

if err != nil {
   return err
}

在滿足業(yè)務(wù)需求的情況下,這種錯誤處理其實是最推薦的方式,這種直接透傳的方式讓代碼之間的耦合度更低。在很多情況下,如果不關(guān)心錯誤中的具體信息,使用這種方式就可以了。

2、提前定義好 error

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

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

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

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

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

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

如果一次性需要處理的錯誤比較多,還可以使用 switch 進行處理:

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

但是這種方式還不完美,因為 error 在傳遞的過程中,有可能會被包裝,以攜帶更多的堆棧信息,比如下面這樣:

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

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

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

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

if errors.Is(err, ErrInvalidParam) {
}

盡量使用 errors.Is 來替代對 error 的比較。

3、使用自定義的錯誤類型

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

type error interface {
    Error() string
}

那么就可以自定義一種錯誤類型:

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

然后就可以使用類型斷言機制或者類型選擇機制,來對不同類型的錯誤進行處理:

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

同樣可以在 switch 中使用:

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

在這里 error 同樣會存在被包裝的問題,而 errors.As 剛好可以用來解決這個問題,可以判斷出被包裝的錯誤中是否存在某個 error 類型:

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

4、更靈活的 error 類型

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

在 Go 的 net 包中,有這樣的一個接口:

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

在這個接口中,有兩個方法,這兩個方法會對這個錯誤類型進行處理,判斷是超時錯誤還是臨時錯誤,實現(xiàn)了這個接口的 error 要實現(xiàn)這兩個 方法,實現(xiàn)具體的判斷邏輯。

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

if ne, ok := e.(net.Error); ok && ne.Temporary() { 
     // 對臨時錯誤進行處理 
}
if ne, ok := e.(net.Error); ok && ne.Timeout() { 
     // 對超時錯誤進行處理 
}

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

5、errors 中的其他能力

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

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

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

在后續(xù)的 error 處理時,可以調(diào)用 errors.Unwrap 函數(shù)來獲得被包裝前的 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 時,應(yīng)該使用統(tǒng)一的 Go 函數(shù)進行創(chuàng)建,這個函數(shù)中會進行 recover ,避免因為野生goroutine panic 導致主進程退出。

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

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

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

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

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

如果是調(diào)用其他庫(標準庫、企業(yè)公共庫、開源第三方庫等)獲取到錯誤時,請使用 errors.Wrap 添加堆棧信息。

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

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

注意我們在基礎(chǔ)庫,在大量引入的第三方庫編寫時一般不使用 errors.Wrap 避免堆棧信息重復(fù)。

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

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

錯誤判斷使用 errors.Is 進行比較:

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

錯誤類型判斷,使用 errors.As 進行賦值:

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

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

相關(guān)文章

  • GO語言支付寶沙箱對接的實現(xiàn)

    GO語言支付寶沙箱對接的實現(xiàn)

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

    Go 計時器使用示例全面講解

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

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

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

    Go 標準庫增加metrics指標探討分析

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

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

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

    Gorm存在時更新,不存在時創(chuàng)建的問題

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

    Go語句與表達式案例手冊深度解析

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

    Go語言中defer語句的用法

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

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

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

    Go Java算法最大單詞長度乘積示例詳解

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

最新評論