在我所見的任何地方,Go 中“包裝”錯誤的“方法”是將 fmt.Errof 與 %w 動詞一起使用
https://go.dev/blog/go1.13-errors
但是, fmt.Errorf 不會遞回地包裝錯誤。無法使用它來包裝三個先前定義的錯誤(Err1、Err2 和 Err3),然后使用 Is() 檢查結果并為這三個錯誤中的每一個都設定為 true。
Go 中是否有一些內置的東西可以正常作業?
我嘗試自己制作一個(幾次嘗試),但遇到了不受歡迎的問題。這些問題源于 Go 中的錯誤似乎是按地址進行比較的事實。即如果 Err1 和 Err2 指向同一個東西,它們是相同的。
這給我帶來了問題。我可以天真地獲取Is()并As()使用自定義錯誤型別遞回地作業。這很簡單。
- 創建一個實作錯誤介面的型別(有一個
Error() string方法) - 該型別必須有一個表示包裝錯誤的成員,該成員是指向其自身型別的指標。
- 實作一個
Unwrap() error回傳包裝錯誤的方法。 - 實作一些方法,將一個錯誤與另一個錯誤包裝起來
看起來不錯。但是有麻煩。
由于錯誤是指標,如果我做類似的事情myWrappedError = Wrap(Err1, Err2)(在這種情況下假設Err1被包裹Err2)。不僅會errors.Is(myWrappedError, Err1)并errors.Is(myWrappedError, Err2)回傳真,但也會如此errors.Is(Err2, Err1)
如果需要 makemyOtherWrappedError = Wrap(Err3, Err2)并稍后呼叫errors.Is(myWrappedError, Err1)它現在將回傳 false!做出myOtherWrappedError改變myWrappedError。
我嘗試了幾種方法,但總是遇到相關問題。
這可能嗎?是否有執行此操作的 Go 庫?
NOTE: I am more interested in the presumably already existing right way to do this rather than the specific thing that is wrong with my basic attempt
Edit 3: As suggested by one of the answers, the issue in my first code is obviously that I modify global errors. I am aware, but failed to adequately communicate. Below, I will include other broken code which uses no pointers and modifies no globals.
Edit 4: slight modification to make it work more, but it is still broken
See https://go.dev/play/p/bSytCysbujX
type errorGroup struct {
err error
wrappedErr error
}
//...implemention Unwrap and Error excluded for brevity
func Wrap(inside error, outside error) error {
return &errorGroup{outside, inside}
}
var Err1 = errorGroup{errors.New("error 1"), nil}
var Err2 = errorGroup{errors.New("error 2"), nil}
var Err3 = errorGroup{errors.New("error 3"), nil}
func main() {
errs := Wrap(Err1, Err2)
errs = Wrap(errs, Err3)
fmt.Println(errs)//error 3: error 2: error 1
fmt.Println(errors.Is(errs, Err1)) //true
fmt.Println(errors.Is(errs, Err2)) //false <--- a bigger problem
fmt.Println(errors.Is(errs, Err3)) //false <--- a bigger problem
}
Edit 2: playground version shortened
See https://go.dev/play/p/swFPajbMcXA for an example of this.
EDIT 1: A trimmed version of my code focusing on the important parts:
type errorGroup struct {
err error
wrappedErr *errorGroup
}
//...implemention Unwrap and Error excluded for brevity
func Wrap(errs ...*errorGroup) (r *errorGroup) {
r = &errorGroup{}
for _, err := range errs {
err.wrappedErr = r
r = err
}
return
}
var Err0 = &errorGroup{errors.New("error 0"), nil}
var Err1 = &errorGroup{errors.New("error 1"), nil}
var Err2 = &errorGroup{errors.New("error 2"), nil}
var Err3 = &errorGroup{errors.New("error 3"), nil}
func main() {
errs := Wrap(Err1, Err2, Err3)//error 3: error 2: error 1
fmt.Println(errors.Is(errs, Err1)) //true
//Creating another wrapped error using the Err1, Err2, or Err3 breaks the previous wrap, errs.
_ = Wrap(Err0, Err2, Err3)
fmt.Println(errors.Is(errs, Err1)) //false <--- the problem
}
uj5u.com熱心網友回復:
你可以使用這樣的東西:
type errorChain struct {
err error
next *errorChain
}
func Wrap(errs ...error) error {
out := errorChain{err: errs[0]}
n := &out
for _, err := range errs[1:] {
n.next = &errorChain{err: err}
n = n.next
}
return out
}
func (c errorChain) Is(err error) bool {
return c.err == err
}
func (c errorChain) Unwrap() error {
if c.next != nil {
return c.next
}
return nil
}
https://go.dev/play/p/6oUGefSxhvF
uj5u.com熱心網友回復:
您的代碼修改了包全域錯誤值,因此它天生就被破壞了。這個缺陷與 Go 的錯誤處理機制無關。
根據您鏈接的檔案,有兩個錯誤處理助手:Is和As. Is讓你遞回地解包錯誤,尋找一個特定的錯誤值,它必須是一個全域包才能有用。As另一方面,允許您遞回地解包錯誤以查找給定型別的任何已包裝錯誤值。
包裝如何作業?您將錯誤 A 包裝在一個新的錯誤值B中。Wrap()幫助器必然會回傳一個新值,就像fmt.Errorf鏈接檔案中的示例一樣。Wrap助手永遠不應該修改被包裝的錯誤的值。該值應該被認為是不可變的。事實上,在任何正常的實作中,該值都是 type error,因此您可以包裝任何錯誤,而不僅僅是將自定義錯誤型別的同心值相互包裝;并且,在這種情況下,您無論如何都無法訪問包裝錯誤的欄位來修改它們。本質上,Wrap應該大致是:
func Wrap(err error) error {
return &errGroup{err}
}
就是這樣。這不是很有用,因為您的實作errGroup實際上并沒有做任何事情 - 它沒有提供有關發生的錯誤的詳細資訊,它只是其他錯誤的容器。為了讓它有價值,它應該有一個string錯誤訊息,或者像其他一些錯誤型別'的方法IsNotFound,或者讓它比僅僅使用error和更有用的東西fmt.Errorf。
根據示例代碼中的用法,您似乎還假設用例是說“我想在 C 中將 A 包裝到 B 中”,這是我從未見過的,我想不出任何需要這樣做的場景。包裝的目的是說“我收到錯誤 A,我將把它包裝在錯誤 B 中以添加背景關系,然后回傳它”。呼叫者可能會將該錯誤包裝在錯誤 C 中,依此類推,這就是遞回包裝有價值的原因。
例如:https ://go.dev/play/p/XeoONx19dgX
uj5u.com熱心網友回復:
有幾種方法,但您應該記住一件事:如果您有多個錯誤,您可能需要將其作為一個錯誤片段來處理
例如,假設您需要檢查是否所有錯誤都相同,或者您可以使用下面的代碼段至少有一個特定型別的錯誤。
您可以擴展這個概念或使用一些現有的庫來處理多重錯誤
type Errors []error
func (errs Errors) String() string {
…
}
func (errs Errors) Any(target error) bool{
for _, err := range errs {
if errors.Is(err,target) {
return true
}
}
return false
}
func (errs Errors) All(target error) bool{
if len(errs) == 0 { return false }
for _, err := range errs {
if !errors.Is(err,target) {
return false
}
}
return true
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/453007.html
標籤:go pointers types error-handling error-checking
下一篇:教程第一步MicrosoftGo
