亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.430618.com 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

由于Golang的語言設計的原因,不管是不是愿意,每個golang開發者的幾乎每一段代碼都需要與error做纏斗。下面我就簡單分析一下golang中的error相關。

轉自:https://www.jianshu.com/p/606d0e60c58d

參考:Go語言中文文檔:www.topgoer.com

error是什么?

首先需要明確的一點是,golang中對于error類型的定義是什么?不同于很多語言的exception機制,golang在語言層面經常需要顯示的做錯誤處理。其實從本質上來講,golang中的error就是一個接口:

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
    Error() string
}

和所有接口含義一樣,nil表示零值。

before Go1.13

在golang的1.13版本之前,官方給到的錯誤處理方法寥寥無幾,只有用來構造無額外參數的錯誤的errors.New和構造帶額外參數的錯誤的fmt.Errorf。當時,經常需要使用標準庫之外的擴展庫來支持更豐富發錯誤構造和處理,比如由Dave Cheney主導的github.com/pkg/errors。
這些額外的error庫主要的關注點在于提供方法用于描述錯誤的層級。回到上面的錯誤本身的定義,只是一個包含Error方法的接口,本身缺乏對于類似其他語言中類似traceback的描述能力,無法追蹤錯誤的詳細棧信息。
而以github.com/pkg/errors為代表的庫,通過實現Wrap和Cause方法對來提供了包裝/拆包錯誤的能力,提供了類似traceback(但需要開發者自己定義額外信息)和逐層解析并比較錯誤的能力。通過這個方法對,我們可以實現下面的用例:

// 為錯誤提供更豐富的上下文信息,方便定位錯誤
if _, err := ioutil.ReadAll(r);err != nil {
        return errors.Wrap(err, "read file failed")
}

// 判斷錯誤的根錯誤是什么,根據最初的錯誤類型判斷需要走什么錯誤處理邏輯
switch err := errors.Cause(err).(type) {
case *io.EOF:
        // handle specifically
default:
        // unknown error
}

After Go1.13

對于上面描述的錯誤處理,相比于較為成熟的exception處理模式,天生缺乏錯誤棧信息的缺點讓很多開發者非常不滿,雖然第三方庫或多或少的彌補了這個缺點,但是作為開發中占比非常大的一部分代碼,官方庫的缺乏支持還是令人不滿。所以Go team在1.13版本中進一步完善了錯誤相關的官方庫支持。
首先,提供了%w構造方法和errors.Unwrap的方法對來支持類似Wrap和Cause相關的能力。

// 為錯誤提供更豐富的上下文信息,方便定位錯誤
if _, err := ioutil.ReadAll(r);err != nil {
        return fmt.Errorf("read file failed with err:%w", err)
}

// 判斷錯誤的根錯誤是什么,根據最初的錯誤類型判斷需要走什么錯誤處理邏輯
rawErr := errors.Unwrap(err)

不僅如此,官方庫還帶來了兩個錯誤比較相關的API:

if errors.Is(err, io.EOF){
    ...
}

var eof io.EOF
if errors.As(err, &eof){
    ...
}

其中,errors.Is方法會逐層調用Unwrap方法,去和目標 err做比較,知道沒有Unwrap方法或者err比較成功。errors.As方法的作用類似于之前的針對錯誤的類型斷言。
至此,golang官方庫提供了錯誤的構造方法,錯誤的比較方法,額外信息包裝的能力,總體來說應該算是比較完善了。
關于Go1.13錯誤處理相關的實現,可以參考。

夭折的try

另外一個小小的番外插曲,曾經有一個呼聲頗高的錯誤處理相關的提案:引入try關鍵字來增強錯誤處理的能力。主要使用方法如下:

// 包裝調用方法
readFile := try(ioutil.ReadAll(r))
...
// 函數層級統一
defer func(){
    if err!=nil{
        switch err.(type){
            ...
        }  
    }
}()

帶來的便利是減少了大量的if err!=nil語句,提供函數層級的統一錯誤處理處(一般在defer處)。然而最后由于可讀性和顯式處理錯誤的種種原因,這個提案被拒絕了。
更近一步的信息可以參考github上相關的討論 和設計文檔。

實踐

基于go1.13提出的現有錯誤處理工具,我們大概能夠采用下面的實踐來進行錯誤處理:

  1. 針對基礎錯誤類型,一般通過直接聲明變量或者自定義結構:
// 常規的無額外參數的error
var BasicErr1 = errors.New("this is a basic error.")

func fn() error{
    ...
    if conditionA{
        return BasicErr
    }
}

// 調用處
if err!=nil{
    if errors.Is(err, BasicErr1){
        ...
    }
}

// 帶參數信息的錯誤
type CustomErr struct {
    Code int64
    Msg string
}

func (e CustomErr)Error() string{
    return fmt.Sprintf("%d:%s", e.Code, e.Msg)
}

func fn() error{
    ...
    if conditionA{
        return CustomErr{Code: 123, Msg: "test"}
    }
}

// 調用處
if err!=nil{
    if e,ok:=err.(CustomErr);ok{
        ...
    }
}
  1. 對于調用三方庫獲取的報錯,一般將額外信息(比如調用參數,上下文信息等方便定位問題的信息)包裝之后向上層調用方直接拋出:
if _,err:=ioutil.ReadAll(r);err!=nil{
    return fmt.Errorf("read file failed:%w", err)
}

// 調用方
if err!=nil{
    if errors.Is(err, io.EOF){
        ...
    }
}

關于錯誤日志的處理部分,為了防止處處打日志造成的上下文信息分散和大量信息冗余,一般建議的處理方式是對于內部方法的調用,使用%w包裝錯誤和必要的額外信息,直接返回到上層;對于最外層方法(一般是http handler或者rpc handler),將錯誤包裝上下文,打印到錯誤日志中,再使用errors.Is或者errors.As方法,根據錯誤類型進行不同的錯誤處理邏輯。這樣的好處是,對于全局而言,有且只有最外層一份錯誤日志,而這個錯誤信息時包裝了層層調用信息的,內容最為齊全。

分享到:
標簽:Golang
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定