Skip to content

Commit

Permalink
generic errors.As() method
Browse files Browse the repository at this point in the history
  • Loading branch information
strider2038 committed Jun 19, 2022
1 parent d3100cc commit 43aaded
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 44 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Key differences and features:
and should be used to create sentinel package-level errors;
* minimalistic API: few methods to wrap an error: `errors.Errorf()`, `errors.Wrap()`;
* adds stack trace idempotently (only once in a chain);
* `errors.As()` method is based on typed parameters (aka generics);
* options to skip caller in a stack trace and to add error fields for structured logging;
* error fields are made for the statically typed logger interface;
* package errors can be easily marshaled into JSON with all fields in a chain.
Expand Down Expand Up @@ -210,7 +211,7 @@ logrusadapter.Log(err, logger)
Output

```
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]"
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}]"
```

## Contributing
Expand Down
38 changes: 23 additions & 15 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,34 @@ func Is(err, target error) bool {
return errors.Is(err, target)
}

// As finds the first error in err's chain that matches target, and if one is found, sets
// target to that error value and returns true. Otherwise, it returns false.
// As finds the first error in err's chain that matches type T, and if one is found, returns
// its value and true. Otherwise, it returns zero value and false.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
// An error matches target if the error's concrete value is of type T, or if the error
// has a method As(any) bool such that As(target) returns true. In the latter case,
// the As method is responsible for setting returned value.
//
// An error type might provide an As method so it can be treated as if it were a
// different error type.
//
// As panics if target is not a non-nil pointer to either a type that implements
// error, or to any interface type.
//
// This function is an alias to standard errors.As.
func As(err error, target interface{}) bool {
return errors.As(err, target)
func As[T any](err error) (T, bool) {
for err != nil {
if t, ok := err.(T); ok {
return t, true
}
if x, ok := err.(interface{ As(any) bool }); ok {
var t T
if x.As(&t) {
return t, true
}
}
err = Unwrap(err)
}

var z T
return z, false
}

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

var w wrapper
_, ok := As[wrapper](err)

return errors.As(err, &w)
return ok
}

type wrapped struct {
Expand Down
Loading

0 comments on commit 43aaded

Please sign in to comment.