arnaud briche
arnaud briche

Reputation: 1509

Check if struct implements a given interface

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

Answers (3)

andygarfield
andygarfield

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

zzzz
zzzz

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{})
}

Playground


Output:

0: CompanyA main.Company -> true
1: CompanyB *main.Company -> true
2: DepartmentA main.Department -> false
3: DepartmentB *main.Department -> true

Upvotes: 32

David Tootill
David Tootill

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

Related Questions