在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請求產生的錯誤的狀態(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ā)送另一個請求,而不是打印一條消息。
包裝錯誤
通常,錯誤會在程序之外產生,例如:數(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接口只有一個方法,我們已經看到我們可以為不同的情況提供不同類型的錯誤。這可以涵蓋從將多條信息作為錯誤的一部分進行溝通到實現(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-12
go語言實現(xiàn)并發(fā)網(wǎng)絡爬蟲的示例代碼
本文主要介紹了go語言實現(xiàn)并發(fā)網(wǎng)絡爬蟲的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-03-03

