亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何包裝錯誤并使其可檢查?

如何包裝錯誤并使其可檢查?

Go
ITMISS 2022-07-25 10:51:42
我正在實現一個使用 TCP 的服務器。讀取函數具有以下簽名func ReadNext() (Message, error)該功能可能因兩個原因而失?。哼B接丟失解析錯誤我的第一反應是用fmt.Errorf(). 但是,我想為該包的使用者提供根據失敗原因進行檢查和調度的能力,因此據我所知,使用內置包裝不提供該選項。我閱讀了不要只是檢查錯誤,優雅地處理它們,發現基本上他提倡使用自定義錯誤類型進行包裝,并在自定義接口上使用類型斷言。我發現這很冗長,需要大量額外的代碼。另外,我看不到與我的錯誤類型相關的行為會使它更清晰:type ParseError interface {  IsParseError() bool}type NetworkError interface {  IsNetworkError() bool}我基本上看到三個選項:不要包裝并返回哨兵錯誤用自定義錯誤類型包裝并進行類型比較Dave Cheney 風格:使用自定義錯誤類型進行包裝,并對描述行為的接口進行斷言。我發現沒有特別好的或優雅的解決方案。有什么建議嗎?
查看完整描述

3 回答

?
烙印99

TA貢獻1829條經驗 獲得超13個贊

錯誤很復雜,因為,嗯,錯誤只是復雜。

Dave Cheney 的博客文章包括以下聲明:

我得出的結論是,沒有單一的方法可以處理錯誤。

我完全同意??紤]一些簡單的事情,比如“從文件中讀取數據”。這可能會失敗。但是為什么它會失敗,你能對這些失敗做些什么嗎?

以下是它可能失敗的一些原因。這并不意味著是所有可能原因的完整列表,只是其中一些原因:

  1. 也許該文件不存在。它應該,但只是沒有。

    您可能對此無能為力。

  2. 也許該文件確實存在,但您沒有訪問它的權限。你應該,但你只是不這樣做。

    除非您具有管理員訪問權限,否則您可能對此無能為力,在這種情況下,您可以授予自己權限并繼續。

  3. 也許該文件存在并且您有權訪問它,但數據已損壞。

    對此您可能無能為力,但如果系統是自我修復的(此類系統存在),這可能是暫時的。

  4. 也許該文件暫時無法訪問,因為它位于網絡上并且網絡已分區。

    這類似于案例 3:等待和重試可能會解決問題(實際上它可能比案例 3 更有可能)。

... [他建議] 包裝自定義錯誤類型并在自定義接口上使用類型斷言。我發現這很冗長,需要大量額外的代碼。

確實如此。它還需要首先產生錯誤的人的合作,以便您可以做出這些細微的區分。那是因為您正在處理真正復雜的硬件和軟件系統。

這不是很令人滿意,但這是真的。幸運的是,對于很多軟件來說,很多錯誤情況都可以歸結為“不是我的問題”:上面的前兩個示例無法打開和讀取某些文件(不存在或權限被拒絕)可以放入這個組。如果您正在編寫文件列表實用程序或類似的東西,這可能沒問題。如果您正在編寫代碼來運行起搏器,則停止并顯示錯誤消息(“致命:無法報告心率”)很可能是,呃,致命的。你需要考慮上下文。


查看完整回答
反對 回復 2022-07-25
?
largeQ

TA貢獻2039條經驗 獲得超8個贊

如果你用 包裝一個錯誤err,fmt.Errorf你可以檢查錯誤errors.Is

例子:

    var ExpectedError = errors.New("This is an error")
    err := fmt.Errorf("I am now wrapping %w ",ExpectedError)
    fmt.Println("Checking error", errors.Is(err, ExpectedError))

https://play.golang.org/p/wtOeopA-f5B


查看完整回答
反對 回復 2022-07-25
?
阿波羅的戰車

TA貢獻1862條經驗 獲得超6個贊

錯誤為不應運行的代碼提供了保護,并提供了處理故障后狀態的方法。


Swift 和 Rust 的學習應用在慣用的 go 風格中:


在應用程序級別,我希望將所有錯誤集中在一個地方,以便我知道該應用程序處理哪些錯誤。


所有關于錯誤的單元測試和應用程序行為都將基于我的應用程序定義的錯誤類型和錯誤變量。為此,我必須編寫從 lib 錯誤到 AppErrors 的轉換,并在應用程序首次檢查錯誤的任何地方使用它們。


因此,如果任何庫更改或被等效庫替換,則只需更改錯誤的轉換(適應)。應用程序錯誤處理策略的其余部分保持原樣并經過測試。


錯誤類型實現接口或者我們只是使用錯誤類型檢查/切換是次要的。


這是該方法的示例。我更喜歡使用 const 來最小化強制轉換。我相信這會更快、更易讀。


package main


import (

    "fmt"

    "os"

)


type ErrorType int


const (

    ReaderEndError ErrorType = iota + 1

    ResourceNotFoundError

)


type AppError struct {

    ErrType ErrorType

    s       string

    error

}


func (ae AppError) Error() string {

    return ae.s

}


func (ae *AppError) GetType() ErrorType {

    return ae.ErrType

}


func main() {


    _, err := readData("notexists")

    if err != nil {

        if ae, ok := err.(AppError); ok {

            //should use switch case in actual startegy

            if ae.ErrType == ResourceNotFoundError {

                if ae.error != nil {

                    fmt.Println("Inner wrapped details (use for logging)", ae.error)

                }


                fmt.Println("handle ResourceNotFoundError here")

            }

        }

    }

}


//sample func

func readData(file string) (*string, error) {

    _, err := os.Open(file)

    if err != nil {

        //switch case here actually

        e, _ := err.(*os.PathError)

        //actually should have many helper conversion functions to do below conversions

        return nil, AppError{ResourceNotFoundError, "Not found", e}

    }


    //assume read data

    data := string("data")

    return &data, nil

}


查看完整回答
反對 回復 2022-07-25
  • 3 回答
  • 0 關注
  • 256 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號