Skip to content

Commit 43aaded

Browse files
committed
generic errors.As() method
1 parent d3100cc commit 43aaded

7 files changed

Lines changed: 283 additions & 44 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Key differences and features:
2121
and should be used to create sentinel package-level errors;
2222
* minimalistic API: few methods to wrap an error: `errors.Errorf()`, `errors.Wrap()`;
2323
* adds stack trace idempotently (only once in a chain);
24+
* `errors.As()` method is based on typed parameters (aka generics);
2425
* options to skip caller in a stack trace and to add error fields for structured logging;
2526
* error fields are made for the statically typed logger interface;
2627
* package errors can be easily marshaled into JSON with all fields in a chain.
@@ -210,7 +211,7 @@ logrusadapter.Log(err, logger)
210211
Output
211212

212213
```
213-
ERRO[0000] find product: sql error: sql: no rows in result set productID=123 requestID=24874020-cab7-4ef3-bac5-76858832f8b0 sql="SELECT id, name FROM product WHERE id = ?" stackTrace="[scratch.go:12 proc.go:250 asm_amd64.s:1571]"
214+
ERRO[0000] find product: sql error: sql: no rows in result set productID=123 requestID=24874020-cab7-4ef3-bac5-76858832f8b0 sql="SELECT id, name FROM product WHERE id = ?" stackTrace="[{main.main /home/strider/projects/errors/var/scratch.go 12} {runtime.main /usr/local/go/src/runtime/proc.go 250} {runtime.goexit /usr/local/go/src/runtime/asm_amd64.s 1571}]"
214215
```
215216

216217
## Contributing

errors.go

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,34 @@ func Is(err, target error) bool {
4646
return errors.Is(err, target)
4747
}
4848

49-
// As finds the first error in err's chain that matches target, and if one is found, sets
50-
// target to that error value and returns true. Otherwise, it returns false.
49+
// As finds the first error in err's chain that matches type T, and if one is found, returns
50+
// its value and true. Otherwise, it returns zero value and false.
5151
//
5252
// The chain consists of err itself followed by the sequence of errors obtained by
5353
// repeatedly calling Unwrap.
5454
//
55-
// An error matches target if the error's concrete value is assignable to the value
56-
// pointed to by target, or if the error has a method As(interface{}) bool such that
57-
// As(target) returns true. In the latter case, the As method is responsible for
58-
// setting target.
55+
// An error matches target if the error's concrete value is of type T, or if the error
56+
// has a method As(any) bool such that As(target) returns true. In the latter case,
57+
// the As method is responsible for setting returned value.
5958
//
6059
// An error type might provide an As method so it can be treated as if it were a
6160
// different error type.
62-
//
63-
// As panics if target is not a non-nil pointer to either a type that implements
64-
// error, or to any interface type.
65-
//
66-
// This function is an alias to standard errors.As.
67-
func As(err error, target interface{}) bool {
68-
return errors.As(err, target)
61+
func As[T any](err error) (T, bool) {
62+
for err != nil {
63+
if t, ok := err.(T); ok {
64+
return t, true
65+
}
66+
if x, ok := err.(interface{ As(any) bool }); ok {
67+
var t T
68+
if x.As(&t) {
69+
return t, true
70+
}
71+
}
72+
err = Unwrap(err)
73+
}
74+
75+
var z T
76+
return z, false
6977
}
7078

7179
// Unwrap returns the result of calling the Unwrap method on err, if err's
@@ -130,9 +138,9 @@ func isWrapper(err error) bool {
130138
return false
131139
}
132140

133-
var w wrapper
141+
_, ok := As[wrapper](err)
134142

135-
return errors.As(err, &w)
143+
return ok
136144
}
137145

138146
type wrapped struct {

0 commit comments

Comments
 (0)