Many developers recommend wrapping errors in Go using fmt.Errorf with the %w verb, but this method does not provide true recursive wrapping. To recursively check errors using Is() and As(), custom error types can be used.
Here's a custom error type errorChain that supports error wrapping and recursive checks:
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 }
The key to enabling recursive checks is to implement both Is() and As() methods on the custom type. These methods allow error comparisons at the error contained within the chain, rather than the chain itself:
func (c errorChain) Is(err error) bool { return errors.Is(c.err, err) } func (c errorChain) As(target any) bool { return errors.As(c.err, target) }
With these methods in place, you can wrap errors and perform recursive checks:
errs := Wrap(errors.New("error 0"), errors.New("error 1"), errors.New("error 2")) fmt.Println(errors.Is(errs, errors.New("error 0"))) // true fmt.Println(errors.Is(errs, errors.New("error 1"))) // true fmt.Println(errors.Is(errs, errors.New("error 2"))) // true
The Unwrap() method in errorChain allows you to traverse the wrapped errors in a chain:
var currentError error = errs for { currentError = errors.Unwrap(currentError) if currentError == nil { break } fmt.Println(currentError) }
This example prints all the errors in the chain:
error 0 error 1 error 2
The above is the detailed content of How Can I Achieve True Recursive Error Wrapping and Unwrapping in Go?. For more information, please follow other related articles on the PHP Chinese website!