diff options
| author | Julien Cretel <jub0bsinthecloud@gmail.com> | 2025-09-29 16:57:53 +0000 |
|---|---|---|
| committer | Damien Neil <dneil@google.com> | 2025-09-30 10:22:08 -0700 |
| commit | a846bb0aa523c8781248161b63bc2ab6a245cec1 (patch) | |
| tree | c43a403c0e8fde2fd9c6019428cddc74710cf4e5 /src/errors/wrap.go | |
| parent | 7c8166d02d36a5dfcdbe3dd1b148412cceacf9f2 (diff) | |
| download | go-a846bb0aa523c8781248161b63bc2ab6a245cec1.tar.xz | |
errors: add AsType
Fixes #51945
Change-Id: Icda169782e796578eba728938134a85b5827d3b6
GitHub-Last-Rev: c6ff335ee1ffb6b7975141795a4632a55247299d
GitHub-Pull-Request: golang/go#75621
Reviewed-on: https://go-review.googlesource.com/c/go/+/707235
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Sean Liao <sean@liao.dev>
Diffstat (limited to 'src/errors/wrap.go')
| -rw-r--r-- | src/errors/wrap.go | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/src/errors/wrap.go b/src/errors/wrap.go index eec9591dae..2ebb951f1d 100644 --- a/src/errors/wrap.go +++ b/src/errors/wrap.go @@ -80,6 +80,10 @@ func is(err, target error, targetComparable bool) bool { // As finds the first error in err's tree that matches target, and if one is found, sets // target to that error value and returns true. Otherwise, it returns false. // +// For most uses, prefer [AsType]. As is equivalent to [AsType] but sets its target +// argument rather than returning the matching error and doesn't require its target +// argument to implement error. +// // The tree consists of err itself, followed by the errors obtained by repeatedly // calling its Unwrap() error or Unwrap() []error method. When err wraps multiple // errors, As examines err followed by a depth-first traversal of its children. @@ -145,3 +149,60 @@ func as(err error, target any, targetVal reflectlite.Value, targetType reflectli } var errorType = reflectlite.TypeOf((*error)(nil)).Elem() + +// AsType finds the first error in err's tree that matches the type E, and +// if one is found, returns that error value and true. Otherwise, it +// returns the zero value of E and false. +// +// The tree consists of err itself, followed by the errors obtained by +// repeatedly calling its Unwrap() error or Unwrap() []error method. When +// err wraps multiple errors, AsType examines err followed by a +// depth-first traversal of its children. +// +// An error err matches the type E if the type assertion err.(E) holds, +// or if the error has a method As(any) bool such that err.As(target) +// returns true when target is a non-nil *E. In the latter case, the As +// method is responsible for setting target. +func AsType[E error](err error) (E, bool) { + if err == nil { + var zero E + return zero, false + } + var pe *E // lazily initialized + return asType(err, &pe) +} + +func asType[E error](err error, ppe **E) (_ E, _ bool) { + for { + if e, ok := err.(E); ok { + return e, true + } + if x, ok := err.(interface{ As(any) bool }); ok { + if *ppe == nil { + *ppe = new(E) + } + if x.As(*ppe) { + return **ppe, true + } + } + switch x := err.(type) { + case interface{ Unwrap() error }: + err = x.Unwrap() + if err == nil { + return + } + case interface{ Unwrap() []error }: + for _, err := range x.Unwrap() { + if err == nil { + continue + } + if x, ok := asType(err, ppe); ok { + return x, true + } + } + return + default: + return + } + } +} |
