Go中有效的錯誤處理

原文鏈接:https://morsmachine.dk/error-handling
翻譯:devabel

介紹

Go得到很多批評的事情之一就是如何處理錯誤。盡管必須明確檢查每個錯誤似乎令人望而生畏,但您可以采取一些措施來防范錯誤的錯誤處理。

以縮進(jìn)流處理錯誤

在編寫Go代碼時(shí),建議:

f, err := os.Open(path)
if err != nil {
    // handle error
}
// do stuff

不建議:

f, err := os.Open(path)
if err == nil {
    // do stuff
}
// handle error

這樣,無錯誤的情況下,業(yè)務(wù)邏輯將直線順序執(zhí)行。

定義你的錯誤

當(dāng)知道有錯誤發(fā)生的時(shí)候必須首先知道怎么傳遞錯誤。如果您的包可能以某種方式導(dǎo)致錯誤,那么您的用戶可能有興趣知道怎么造成了錯誤。要做到這一點(diǎn),你只需要實(shí)現(xiàn)錯誤接口,就是這樣簡單的事情:

type Error string

func (e Error) Error() string { return string(e) }

使用您得包的用戶現(xiàn)在可以通過執(zhí)行類型斷言來判斷您的包是否導(dǎo)致錯誤

result, err := yourpackage.Foo()
if ype, ok := err.(yourpackage.Error); ok {
    // use ype to handle error
}

這也可以用來向用戶公開結(jié)構(gòu)化錯誤信息。

type ParseError struct {
    File  *File
    Error string
}

func (oe *OpenError) Error() string {
    // format error string here
}

func ParseFiles(files []*File) error {
    for _, f := range files {
        err := f.parse()
        if err != nil {
            return &OpenError{
                File:  f,
                Error: err.Error(),
            }
        }
    }
}

這樣,您的用戶現(xiàn)在可以分辨哪個文件無法解析。

盡管如此,你應(yīng)該小心包裝錯誤。當(dāng)您包裝錯誤時(shí),信息可能會丟失。

var c net.Conn
f, err := DownloadFile(c, path)
switch e := err.(type) {
default:
    // this will get executed if err == nil
case net.Error:
    // close connection, not valid anymore
    c.Close()
    return e
case error:
    // if err is non-nil
    return err
}
// do other things.

如果你包裝net.Error,這段代碼將不會看到它是網(wǎng)絡(luò)失敗并重新使用無效連接。

一個好的經(jīng)驗(yàn)法則是,如果你的軟件包使用外部接口,不要將通過調(diào)用產(chǎn)生的錯誤包裝起來。您的用戶可能比您的更關(guān)心他們的錯誤。

狀態(tài)錯誤

有時(shí)候您可能想要堅(jiān)持發(fā)生錯誤,因?yàn)槟梢匝舆t報(bào)告,或者您知道很快會再次報(bào)告。

第一種情況的一個好例子是bufio包。當(dāng)bufio.Reader遇到錯誤時(shí),它會一直等到錯誤發(fā)生,直到緩沖區(qū)被清空。只有這樣它才會報(bào)告它。

第二種情況的一個好例子是go / loader。當(dāng)使用導(dǎo)致錯誤的參數(shù)進(jìn)行調(diào)用時(shí),它會保留錯誤,因?yàn)樗芸赡軙俅斡孟嗤膮?shù)調(diào)用。

使用函數(shù)來避免重復(fù)
如果你有一個重復(fù)的錯誤處理,你可以使用一個函數(shù)。

func handleError(c net.Conn, err error) {
    // repeated error handling
}

func DoStuff(c net.Conn) error {
    f, err := downloadFile(c, path)
    if err != nil {
        handleError(c, err)
        return err
    }
    
    f, err := doOtherThing(c)
    if err != nil {
        handleError(c, err)
        return err
    }
}

另一種寫作方式是

func handleError(c net.Conn, err error) {
    if err == nil {
        return
    }
    // repeated error handling
}

func DoStuff(c net.Conn) error {
    defer func() { handleError(c, err) }()
    f, err := downloadFile(c, path)
    if err != nil {
        return err
    }
    
    f, err := doOtherThing(c)
    if err != nil {
        return err
    }
}

就這樣。
這就是它的全部。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容