Go語言標(biāo)準(zhǔn)錯誤error全面解析
錯誤類型
errorString
錯誤是程序中處理邏輯和系統(tǒng)穩(wěn)定新的重要組成部分。
在go語言中內(nèi)置錯誤如下:
// The error built-in interface type is the conventional interface for// representing an error condition, with the nil value representing no error.type error interface { Error() string}
error
類型是一個接口類型,內(nèi)含一個Error
的方法。它是錯誤的頂級接口,實現(xiàn)了此內(nèi)置方法的結(jié)構(gòu)體都是其子類。
errorString
結(jié)構(gòu)體是內(nèi)置實現(xiàn)錯誤接口的內(nèi)置實現(xiàn),源碼如下:
// New returns an error that formats as the given text. // Each call to New returns a distinct error value even if the text is identical. func New(text string) error { return &errorString{text} } // errorString is a trivial implementation of error. type errorString struct { s string } func (e *errorString) Error() string { return e.s }
New
方法是內(nèi)置錯誤類型實現(xiàn)。
errors.New()是最常用的錯誤類實現(xiàn)方法。
wrapError
wrapError
是error的另一種實現(xiàn)類,位于fmt
的包中,源碼如下:
// Errorf formats according to a format specifier and returns the string as a // value that satisfies error. // // If the format specifier includes a %w verb with an error operand, // the returned error will implement an Unwrap method returning the operand. // If there is more than one %w verb, the returned error will implement an // Unwrap method returning a []error containing all the %w operands in the // order they appear in the arguments. // It is invalid to supply the %w verb with an operand that does not implement // the error interface. The %w verb is otherwise a synonym for %v. func Errorf(format string, a ...any) error { p := newPrinter() p.wrapErrs = true p.doPrintf(format, a) s := string(p.buf) var err error switch len(p.wrappedErrs) { case 0: err = errors.New(s) case 1: w := &wrapError{msg: s} w.err, _ = a[p.wrappedErrs[0]].(error) err = w default: if p.reordered { slices.Sort(p.wrappedErrs) } var errs []error for i, argNum := range p.wrappedErrs { if i > 0 && p.wrappedErrs[i-1] == argNum { continue } if e, ok := a[argNum].(error); ok { errs = append(errs, e) } } err = &wrapErrors{s, errs} } p.free() return err } type wrapError struct { msg string err error } func (e *wrapError) Error() string { return e.msg } func (e *wrapError) Unwrap() error { return e.err }
wrapError
是另一個內(nèi)置錯誤實現(xiàn)類,使用%w
作為占位符,這里的wrapError實現(xiàn)了Unwrap
方法,用戶返回內(nèi)置的err即嵌套的err。
wrapError還有一個復(fù)數(shù)形式wrapErrors
這里不再過多贅述。
- 自定義錯誤
實現(xiàn)自定義錯誤非常簡單,面向?qū)ο蟮奶匦詫崿F(xiàn)錯誤接口erros就是實現(xiàn)了錯誤類。安裝go語言的繼承的特性,實現(xiàn)接口對應(yīng)的方法即可。
type error interface { Error() string }
type MyErr struct { e string } func (s *MyErr) Error() string { return s.e } func main(){ var testErr error testErr = &MyErr{"err"} fmt.Println(testErr.Error()) }
上述代碼就是實現(xiàn)了一個自定義的error類型,注意它是一個結(jié)構(gòu)體,實際上是errorString
的子類。
新建錯誤
上一小節(jié)介紹了三種錯誤類型,前兩中是內(nèi)置的錯誤類型,其中自定義的錯誤是可拓展的,可以實現(xiàn)前兩種的任意一個。
第一種errorString
是實現(xiàn)比較方便,只有實現(xiàn)Error()
方法;
第二種是wrapError
需要實現(xiàn)兩種方法,還有一種是Unwrap()
。
errors.New()
err := errors.New("this is a error") fmt.Printf("----%T----%v\n", err, err)
該方法創(chuàng)建的是errorString
類實例
fmt.Errorf()
err = fmt.Errorf("err is: %v", "no found") fmt.Println(err)
該方法創(chuàng)建的是wrapError
類實例,wrapError也是errorString的子類。
實例化
type MyErr struct { e string } func (s *MyErr) Error() string { return s.e } func main(){ var testErr error testErr = &MyErr{"err"} fmt.Println(testErr.Error()) }
由于自定義錯誤一般需要錯誤信息,所以一般直接構(gòu)造方法實例化。
錯誤解析
errors.Is()
errors.Is
用于判斷一個錯誤是否與另一個特定的錯誤相等。它不僅僅是簡單的比較錯誤的值,還會檢查錯誤鏈中的所有錯誤,看看它們是否與給定的目標(biāo)錯誤匹配。
package main import ( "errors" "fmt" ) var ErrNotFound = errors.New("not found") func findItem(id int) error { if id == 0 { return ErrNotFound } return nil } func main() { err := findItem(0) if errors.Is(err, ErrNotFound) { fmt.Println("Item not found") } else { fmt.Println("Item found") } }
注意這里有一個坑,就是Is方法判斷的錯誤的類型和錯誤的信息,使用New方法即使構(gòu)建的錯誤信息相同類型不一樣也是不相等的,如下:
err1 := errors.New("err1") err2 := errors.New("err1") err := errors.Is(err1, err2) fmt.Println(err) // 輸出: false
errors.As()
errors.As
用于將一個錯誤轉(zhuǎn)換為特定的錯誤類型。如果錯誤鏈中的某個錯誤匹配給定的目標(biāo)類型,那么 errors.As 會將該錯誤轉(zhuǎn)換為該類型,并將其賦值給目標(biāo)變量。
package main import ( "errors" "fmt" ) type MyError struct { Code int Msg string } func (e *MyError) Error() string { return fmt.Sprintf("code %d: %s", e.Code, e.Msg) } func doSomething() error { return &MyError{Code: 404, Msg: "Not Found"} } func main() { err := doSomething() var myErr *MyError if errors.As(err, &myErr) { fmt.Printf("Custom error: %v (Code: %d)\n", myErr.Msg, myErr.Code) } else { fmt.Println("Unknown error") } }
可用作類型判斷。
errors.Unwrap()
errors.Unwrap()
是一個用于處理嵌套或包裝錯誤的函數(shù)。它的主要作用是提取并返回一個錯誤的直接底層錯誤(即被包裝的錯誤),如果該錯誤沒有被包裝過,則返回 nil
。
package main import ( "errors" "fmt" ) func main() { baseErr := errors.New("base error") wrappedErr := fmt.Errorf("wrapped error: %w", baseErr) // 使用 errors.Unwrap 來提取底層錯誤 unwrappedErr := errors.Unwrap(wrappedErr) fmt.Println("Wrapped error:", wrappedErr) fmt.Println("Unwrapped error:", unwrappedErr) } // Output Wrapped error: wrapped error: base error Unwrapped error: base error
該方法只能獲取錯誤的直接內(nèi)嵌錯誤,如果要獲取更深層次的錯誤需要便利判斷。
注意:
errors.Is
: 用于判斷一個錯誤是否與特定的錯誤值相等。適合在錯誤鏈中查找某個特定的錯誤(比如一個已知的預(yù)定義錯誤)。errors.As
: 用于將錯誤轉(zhuǎn)換為特定的錯誤類型。適合當(dāng)你需要根據(jù)錯誤的具體類型來處理錯誤時使用。errors.Unwrap()
用于從一個包裝錯誤中提取并返回底層的錯誤。如果錯誤沒有被包裝過(或者沒有實現(xiàn) Unwrap 方法),它會返回nil
。
錯誤處理
if err := findAll(); err != nil { // logic }
這個處理過程是不是很眼熟,利用錯誤解析小節(jié)的處理方法可以對錯誤判斷,進(jìn)行后續(xù)的處理。
go語言也提供了錯誤捕獲recover
的機制和錯誤拋出panic
機制。
panic
panic
是 Go 語言中的一種觸發(fā)異常處理機制的方式。當(dāng)你調(diào)用 panic 時,程序會立刻停止執(zhí)行當(dāng)前函數(shù),并從調(diào)用棧中逐層向上拋出,直到找到一個適合的 recover,或者最終導(dǎo)致程序崩潰。
package main import "fmt" func main() { fmt.Println("Start") panic("Something went wrong!") fmt.Println("End") // This line will not be executed }
當(dāng)程序中調(diào)用了 panic
,程序會立刻中止當(dāng)前的控制流,開始回溯棧
幀,并執(zhí)行每一層的 defer
語句。在執(zhí)行完所有的 defer 語句后,如果沒有遇到 recover
,程序?qū)⒈罎ⅲ⒋蛴?panic 的信息和堆棧跟蹤。
recover
recover
是一個內(nèi)建函數(shù),用于恢復(fù) panic。它只能在 defer
函數(shù)中有效。當(dāng) panic 發(fā)生時,如果當(dāng)前函數(shù)
或任何調(diào)用棧
上的函數(shù)中有 defer 函數(shù)調(diào)用了 recover,那么可以捕獲 panic 的內(nèi)容,并使程序恢復(fù)正常執(zhí)行。
package main import "fmt" func main() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from:", r) } }() fmt.Println("Start") panic("Something went wrong!") fmt.Println("End") // This line will not be executed }
recover 通常用于確保某些代碼塊即使發(fā)生了 panic,也能執(zhí)行資源清理操作,并避免整個程序崩潰。
defer
defer
語句用于延遲函數(shù)的執(zhí)行,直到包含 defer 語句的函數(shù)執(zhí)行完畢時,才會執(zhí)行。這通常用于確保資源釋放或清理操作(如關(guān)閉文件、解鎖互斥鎖等)即使在函數(shù)中發(fā)生錯誤或提前返回時也會被執(zhí)行。
- 執(zhí)行順序: 在一個函數(shù)中,你可以有多個 defer 語句。這些 defer 調(diào)用的執(zhí)行順序是后進(jìn)先出(LIFO)。也就是說,最后一個 defer 聲明的語句會最先執(zhí)行。
- 捕獲值: defer 語句會在聲明時捕獲它引用的變量的值。也就是說,defer 語句中的參數(shù)會在聲明 defer 時計算,而不是在 defer 執(zhí)行時計算。
三個函數(shù)組合構(gòu)成了錯誤處理,如下:
package main import "fmt" func main() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() fmt.Println("Starting the program") causePanic() fmt.Println("Program ended gracefully") } func causePanic() { fmt.Println("About to cause panic") panic("A severe error occurred") fmt.Println("This line will not execute") }
Starting the program About to cause panic Recovered from panic: A severe error occurred
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
GoLang并發(fā)編程中條件變量sync.Cond的使用
Go標(biāo)準(zhǔn)庫提供Cond原語的目的是,為等待/通知場景下的并發(fā)問題提供支持,本文主要介紹了Go并發(fā)編程sync.Cond的具體使用,具有一定的參考價值,感興趣的可以了解一下2023-01-01Go語言讀取,設(shè)置Cookie及設(shè)置cookie過期方法詳解
這篇文章主要介紹了Go語言讀取,設(shè)置Cookie及設(shè)置cookie過期方法詳解,需要的朋友可以參考下2022-04-04Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法詳解
這篇文章主要介紹了Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法2024-01-01