Reputation: 1509
I need to walk through all the field of a struct type and check if they implements a given interface.
type Model interface {...}
func HasModels(m Model) {
s := reflect.ValueOf(m).Elem()
t := s.Type()
modelType := reflect.TypeOf((*Model)(nil)).Elem()
for i := 0; i < s.NumField(); i++ {
f := t.Field(i)
fmt.Printf("%d: %s %s -> %s\n", i, f.Name, f.Type, f.Type.Implements(modelType))
}
}
Then, if a call HasModels with a struct like so :
type Company struct {...}
type User struct {
...
Company Company
}
HasModels(&User{})
With Company and User both implementing Model; I get f.Type.Implements(ModelType) returning false for the Company field of the User struct.
This is unexpected, so, what Am I doing wrong here ?
Upvotes: 21
Views: 22127
Reputation: 21
If the variable is not an interface, make it one.
foo := interface{}(yourVar)
Then you can check if it implements the interface you're interested in.
bar, ok := foo.(yourInterface)
if !ok {
// handle the case where it doesn't match the interface
}
bar.methodOnYourInterface()
Here's a playground link for a more fleshed out example
Upvotes: 2
Reputation: 91203
You've unfortunately left out the essential parts (please always post complete programs), so I can only guess that the problem is in a method defined on a pointer receiver, in which case the behavior of your code is expected. Check this example and its output:
package main
import (
"fmt"
"reflect"
)
type Model interface {
m()
}
func HasModels(m Model) {
s := reflect.ValueOf(m).Elem()
t := s.Type()
modelType := reflect.TypeOf((*Model)(nil)).Elem()
for i := 0; i < s.NumField(); i++ {
f := t.Field(i)
fmt.Printf("%d: %s %s -> %t\n", i, f.Name, f.Type, f.Type.Implements(modelType))
}
}
type Company struct{}
func (Company) m() {}
type Department struct{}
func (*Department) m() {}
type User struct {
CompanyA Company
CompanyB *Company
DepartmentA Department
DepartmentB *Department
}
func (User) m() {}
func main() {
HasModels(&User{})
}
Output:
0: CompanyA main.Company -> true
1: CompanyB *main.Company -> true
2: DepartmentA main.Department -> false
3: DepartmentB *main.Department -> true
Upvotes: 32
Reputation: 571
There's an easier way of doing this that doesn't need reflect. For example:
type middlewarer interface {Middleware() negroni.Handler}
for _, controller := range ctrls {
if m, ok := interface{}(controller).(middlewarer); ok {
n.Use(m.Middleware())
}
}
calls the Middleware() method only in those slice elements that implement the middlewarer interface.
Upvotes: 13