核心思想:套娃
啥意思呢?這玩意就像套娃一樣,從上往下扒,拿走一個還有一個,再拿走一個,誒還有一個,如果你願意,可以一直扒到最底下沒有了為止。
基本用法
1. 創建一個被包裝的 error
方式一:fmt.Errorf
使用 %w
參數返回一個被包裝的 error
err1 := errors.New("new error") err2 := fmt.Errorf("err2: [%w]", err1) err3 := fmt.Errorf("err3: [%w]", err2) fmt.Println(err3)
// output
err3: [err2: [new error]]
err2
就是一個合法的被包裝的 error,同樣地,err3
也是一個被包裝的 error,如此可以一直套下去。
方式二:自定義 struct
type WarpError struct { msg string err error } func (e *WarpError) Error() string { return e.msg } func (e *WrapError) Unwrap() error { return e.err }
之前看過源碼的同學可能已經知道了,這就是 fmt/errors.go
中關於 warp 的結構。
就,很簡單。自定義一個實現了 Unwrap
方法的 struct 就可以了。
2. 拆開一個被包裝的 error
errors.Unwrap
err1 := errors.New("new error") err2 := fmt.Errorf("err2: [%w]", err1) err3 := fmt.Errorf("err3: [%w]", err2) fmt.Println(errors.Unwrap(err3)) fmt.Println(errors.Unwrap(errors.Unwrap(err3)))
// output
err2: [new error]
new error
3. 判斷被包裝的 error 是否是包含指定錯誤
errors.Is
當多層調用返回的錯誤被一次次地包裝起來,我們在調用鏈上游拿到的錯誤如何判斷是否是底層的某個錯誤呢?它遞歸調用 Unwrap 並判斷每一層的 err 是否相等,如果有任何一層 err 和傳入的目標錯誤相等,則返回 true。
err1 := errors.New("new error") err2 := fmt.Errorf("err2: [%w]", err1) err3 := fmt.Errorf("err3: [%w]", err2) fmt.Println(errors.Is(err3, err2)) fmt.Println(errors.Is(err3, err1))
// output
true
true
4. 提取指定類型的錯誤
errors.As
這個和上面的 errors.Is
大體上是一樣的,區別在於 Is
是嚴格判斷相等,即兩個 error
是否相等。
而 As
則是判斷類型是否相同,並提取第一個符合目標類型的錯誤,用來統一處理某一類錯誤。
type ErrorString struct { s string } func (e *ErrorString) Error() string { return e.s } var targetErr *ErrorString err := fmt.Errorf("new error:[%w]", &ErrorString{s:"target err"}) fmt.Println(errors.As(err, &targetErr))
// output
true
擴展
Is
As
兩個方法已經預留了口子,可以由自定義的 error struct 實現並覆蓋調用。