Description
The interfaces for errors.Is
and errors.As
seem stable and complete. Unfortunately, it requires reading through the godoc and use of a custom type to ensure that your custom error type implements interface{ As(interface{}) bool }
or interface{ Is(error) bool }
.
I propose exposing the following symbols:
package errors
// Aser is the interface implemented by types that can be converted into other types.
//
// See As for full usage.
type Aser interface{ As(interface{}) bool }
// Iser is the interface implemented by types that are comparable to other errors.
//
// See Is for full usage.
type Iser interface{ Is(error) bool }
And fixing up errors.Is
and errors.As
to document and use them:
--- a/src/errors/wrap.go
+++ b/src/errors/wrap.go
@@ -27,7 +27,7 @@
// repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if
-// it implements a method Is(error) bool such that Is(target) returns true.
+// it implements Iser such that Is(target) returns true.
//
// An error type might provide an Is method so it can be treated as equivalent
// to an existing error. For example, if MyError defines
@@ -46,7 +46,7 @@
if isComparable && err == target {
return true
}
- if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
+ if x, ok := err.(Iser); ok && x.Is(target) {
return true
}
// TODO: consider supporting target.Is(err). This would allow
@@ -65,9 +65,9 @@
// 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.
+// pointed to by target, or if the error implements Aser such that As(target)
+// returns true. In the latter case, the As method is responsible for setting
+// target.
//
// An error type might provide an As method so it can be treated as if it were a
// a different error type.
@@ -92,7 +92,7 @@
val.Elem().Set(reflectlite.ValueOf(err))
return true
}
- if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
+ if x, ok := err.(Aser); ok && x.As(target) {
return true
}
err = Unwrap(err)
This allows error
implementers to ensure that the interfaces are implemented correctly, while also cleaning up errors
documentation and internal logic:
var _, _, _ = errors.Iser(myError{}), errors.Aser(myError{}), error(myError{})
That being said, the names Iser
and Aser
sound like fantasy characters and are not strictly required, since they do not exist today and errors.Is
and errors.As
work fine. Furthermore, I cannot think of a reason to pass, accept, or typecast these interfaces, so their usefulness is limited. That being said, json.Marshaler
suffers from similar issues, yet it exists.