原文鏈接: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
}
}
就這樣。
這就是它的全部。