在Go中創(chuàng)建自定義錯誤的方式總結
引言
Go提供了兩種在標準庫中創(chuàng)建錯誤的方法,[errors.New
和fmt.Errorf
],當與用戶交流更復雜的錯誤信息時,或在調試時與未來的自己交流時,有時這兩種機制不足以充分捕獲和報告所發(fā)生的情況。為了傳達更復雜的錯誤信息并實現(xiàn)更多的功能,我們可以實現(xiàn)標準庫接口類型,error。
其語法如下:
type error interface { Error() string }
builtin包將error
定義為一個接口,它只有一個Error()
方法,返回一個字符串形式的錯誤消息。通過實現(xiàn)這個方法,我們可以將定義的任何類型轉換為我們自己的error。
讓我們嘗試運行以下示例來查看error
接口的實現(xiàn):
package main import ( "fmt" "os" ) type MyError struct{} func (m *MyError) Error() string { return "boom" } func sayHello() (string, error) { return "", &MyError{} } func main() { s, err := sayHello() if err != nil { fmt.Println("unexpected error: err:", err) os.Exit(1) } fmt.Println("The string:", s) }
Outputunexpected error: err: boom exit status 1
在這里,我們創(chuàng)建了一個新的空結構類型MyError
,并在其上定義了Error()
方法。Error()
方法返回字符串"boom"
。
在main()
中,我們調用函數(shù)sayHello
,該函數(shù)返回一個空字符串和一個新的MyError
實例。由于sayHello
總是會返回錯誤,所以main()
中的if語句體中的fmt.Println
調用總是會執(zhí)行。然后,我們使用fmt.Println
打印短前綴字符串"unexpected error:"
以及err
變量中保存的MyError
實例。
請注意,我們不必直接調用Error()
,因為fmt
包能夠自動檢測這是Error
的實現(xiàn)。它透明地調用Error()
來獲取字符串"boom"
,并將其與前綴字符串"unexpected Error: err:"
連接起來。
在自定義錯誤中收集詳細信息
有時候,自定義錯誤是捕獲詳細錯誤信息的最簡潔方式。例如,假設我們想要捕獲HTTP請求產(chǎn)生的錯誤的狀態(tài)碼;運行以下程序來查看error
的實現(xiàn),它允許我們清晰地捕獲信息:
package main import ( "errors" "fmt" "os" ) type RequestError struct { StatusCode int Err error } func (r *RequestError) Error() string { return fmt.Sprintf("status %d: err %v", r.StatusCode, r.Err) } func doRequest() error { return &RequestError{ StatusCode: 503, Err: errors.New("unavailable"), } } func main() { err := doRequest() if err != nil { fmt.Println(err) os.Exit(1) } fmt.Println("success!") }
Outputstatus 503: err unavailable
exit status 1
在這個例子中,我們創(chuàng)建了一個新的RequestError
實例,并使用標準庫中的errors.New
函數(shù)提供狀態(tài)碼和一個錯誤。然后像前面的例子一樣,我們使用fmt.Println
打印它。
在RequestError
的Error()
方法中,我們使用fmt.Sprintf
函數(shù)來使用創(chuàng)建錯誤時提供的信息來構造一個字符串。
類型斷言和自定義錯誤
error
接口只公開了一個方法,但我們可能需要訪問error
實現(xiàn)的其他方法來正確處理錯誤。例如,我們可能有幾個臨時的error
自定義實現(xiàn),可以通過存在的 temporary()
方法進行檢索。
接口為類型提供了更廣泛的方法集合,因此我們必須使用類型斷言來更改view正在顯示的方法,或者完全刪除它。
下面的例子在之前的RequestError
的基礎上增加了一個Temporary()
方法,該方法將表明調用者是否應該重試請求:
package main import ( "errors" "fmt" "net/http" "os" ) type RequestError struct { StatusCode int Err error } func (r *RequestError) Error() string { return r.Err.Error() } func (r *RequestError) Temporary() bool { return r.StatusCode == http.StatusServiceUnavailable // 503 } func doRequest() error { return &RequestError{ StatusCode: 503, Err: errors.New("unavailable"), } } func main() { err := doRequest() if err != nil { fmt.Println(err) re, ok := err.(*RequestError) if ok { if re.Temporary() { fmt.Println("This request can be tried again") } else { fmt.Println("This request cannot be tried again") } } os.Exit(1) } fmt.Println("success!") }
Outputunavailable
This request can be tried again
exit status 1
在main()
中,我們調用doRequest()
,它會返回一個error
接口給我們。我們首先打印 error()
方法返回的錯誤消息。接下來,我們嘗試使用類型斷言re, ok := err.(*RequestError)
來暴露RequestError
中的所有方法。如果類型斷言成功,那么我們使用Temporary()
方法來查看此錯誤是否為臨時錯誤。由于doRequest()
設置的StatusCode
是503
,這與http.StatusServiceUnavailable
匹配,因此返回true
并導致打印" this request can be try again"
。在實踐中,我們會發(fā)送另一個請求,而不是打印一條消息。
包裝錯誤
通常,錯誤會在程序之外產(chǎn)生,例如:數(shù)據(jù)庫、網(wǎng)絡連接等。這些錯誤提供的錯誤消息不能幫助任何人找到錯誤的根源。在錯誤消息的開頭使用額外的信息包裝錯誤,可以為成功調試提供一些必要的上下文。
下面的例子演示了我們如何將一些上下文信息附加到從其他函數(shù)返回的晦澀的error
上:
package main import ( "errors" "fmt" ) type WrappedError struct { Context string Err error } func (w *WrappedError) Error() string { return fmt.Sprintf("%s: %v", w.Context, w.Err) } func Wrap(err error, info string) *WrappedError { return &WrappedError{ Context: info, Err: err, } } func main() { err := errors.New("boom!") err = Wrap(err, "main") fmt.Println(err) }
Outputmain: boom!
WrappedError
是一個有兩個字段的結構體:一個是string
類型的上下文消息,另一個是error
類型,WrappedError
提供了更多的信息。當Error()
方法被調用時,我們再次使用fmt.Sprintf
來打印上下文消息,然后Error
(fmt.Sprintf
也知道隱式調用Error()
方法)。
在main()
中,我們使用errors.New
創(chuàng)建一個錯誤,然后使用我們定義的wrap
函數(shù)包裝這個錯誤。這允許我們指出這個error
是在"main"
中生成的。此外,由于我們的WrappedError
也是一個error
,我們可以包裝其他的WrappedError
,這將允許我們看到一個鏈來幫助我們追蹤錯誤的來源。在標準庫的幫助下,我們甚至可以在錯誤中嵌入完整的堆棧跟蹤。
總結
由于error
接口只有一個方法,我們已經(jīng)看到我們可以為不同的情況提供不同類型的錯誤。這可以涵蓋從將多條信息作為錯誤的一部分進行溝通到實現(xiàn)指數(shù)回退的所有事情。雖然Go中的錯誤處理機制表面上看起來很簡單,但我們可以使用這些自定義錯誤來處理常見和不常見的情況,從而實現(xiàn)相當豐富的處理。
Go還有另一種溝通意外行為的機制panics。在錯誤處理系列的下一篇文章中,我們將研究恐慌——它們是什么以及如何處理它們。
到此這篇關于在Go中創(chuàng)建自定義錯誤的文章就介紹到這了,更多相關Go創(chuàng)建自定義錯誤內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
go中的參數(shù)傳遞是值傳遞還是引用傳遞的實現(xiàn)
參數(shù)傳遞機制是一個重要的概念,它決定了函數(shù)內部對參數(shù)的修改是否會影響到原始數(shù)據(jù),本文主要介紹了go中的參數(shù)傳遞是值傳遞還是引用傳遞的實現(xiàn),感興趣的可以了解一下2024-12-12go語言實現(xiàn)并發(fā)網(wǎng)絡爬蟲的示例代碼
本文主要介紹了go語言實現(xiàn)并發(fā)網(wǎng)絡爬蟲的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-03-03