Reputation: 57451
I'd like to write some code which inspects the methods of a struct and makes certain assertions on them, for example, that the last thing returned by them should be an error
. I've tried the following example script:
import (
"context"
"reflect"
)
type Service struct {
name string
}
func (svc *Service) Handle(ctx context.Context) (string, error) {
return svc.name, nil
}
func main() {
s := &Service{}
t := reflect.TypeOf(s)
for i := 0; i < t.NumMethod(); i++ {
f := t.Method(i).Func.Type()
f.Out(f.NumOut() - 1).Implements(reflect.TypeOf(error))
}
}
However, this yields a
./main.go:23:51: type error is not an expression
What does compile are instead the following two lines at the end:
var err error
f.Out(f.NumOut() - 1).Implements(reflect.TypeOf(err))
However, this yields a panic:
panic: reflect: nil type passed to Type.Implements
What would be the correct way to check that the last arguments implements the error
interface? In other words, how do I get a reflect.Type
of an error
interface?
Upvotes: 2
Views: 1243
Reputation: 38213
If the last return value "should be" and error
do not use Implements
, that's not sufficient, x implements e is not the same as x is e.
Just check the type's name and package path. For predeclared types, including error
, the package path is an empty string.
A non-error type that implements error
.
type Service struct {
name string
}
type sometype struct {}
func (sometype) Error() string { return "" }
func (svc *Service) Handle(ctx context.Context) (string, sometype) {
return svc.name, sometype{}
}
func main() {
s := &Service{}
t := reflect.TypeOf(s)
for i := 0; i < t.NumMethod(); i++ {
f := t.Method(i).Func.Type()
rt := f.Out(f.NumOut() - 1)
fmt.Printf("implements error? %t\n", rt.Implements(reflect.TypeOf((*error)(nil)).Elem()))
fmt.Printf("is error? %t\n", rt.Name() == "error" && rt.PkgPath() == "")
}
}
This outputs:
implements error? true
is error? false
A locally declared type named error
that doesn't implement the builtin error
.
type Service struct {
name string
}
type error interface { Abc() }
func (svc *Service) Handle(ctx context.Context) (string, error) {
return svc.name, nil
}
type builtin_error interface { Error() string }
func main() {
s := &Service{}
t := reflect.TypeOf(s)
for i := 0; i < t.NumMethod(); i++ {
f := t.Method(i).Func.Type()
rt := f.Out(f.NumOut() - 1)
fmt.Printf("implements error? %t\n", rt.Implements(reflect.TypeOf((*builtin_error)(nil)).Elem()))
fmt.Printf("is error? %t\n", rt.Name() == "error" && rt.PkgPath() == "")
}
}
This outputs:
implements error? false
is error? false
The actual builtin error
.
type Service struct {
name string
}
func (svc *Service) Handle(ctx context.Context) (string, error) {
return svc.name, nil
}
func main() {
s := &Service{}
t := reflect.TypeOf(s)
for i := 0; i < t.NumMethod(); i++ {
f := t.Method(i).Func.Type()
rt := f.Out(f.NumOut() - 1)
fmt.Printf("implements error? %t\n", rt.Implements(reflect.TypeOf((*error)(nil)).Elem()))
fmt.Printf("is error? %t\n", rt.Name() == "error" && rt.PkgPath() == "")
}
}
This outputs:
implements error? true
is error? true
Upvotes: 4
Reputation: 22037
To get the reflect.TypeOf
of an error
without using an existing error, you can use this one-liner:
reflect.TypeOf((*error)(nil)).Elem()
Basically, it first gets type of a pointer to error (*error
) and then Elem()
"deferences" the TypeOf to the type of error
.
Upvotes: 2
Reputation: 51567
Use a pointer to interface, and get the Elem
of it, like this:
f.Out(f.NumOut() - 1).Implements(reflect.TypeOf((*error)(nil)).Elem())
Upvotes: 2