简体   繁体   中英

How do I properly check whether a struct field implements an interface when it requires a pointer receiver?

I'm trying to determine if a field on a struct implements a given interface that has a pointer receiver.

I have a go playground setup with my confusion .

Say I have the following interface :

type Stringee interface {
    FromString(s string) error
}

With an enum implementing it:

type Enum int

const (
    EnumUnknown Enum = iota
    EnumA
    EnumB
)

func (my *Enum) String() string { return enumToStr[*my] }

func (my *Enum) FromString(s string) error {
    if v, ok := enumFromStr[s]; ok {
        *my = v
        return nil
    }
    return errors.New("invalid value")
}

var enumToStr = map[Enum]string{
    EnumA: "A",
    EnumB: "B",
}
var enumFromStr = func() map[string]Enum {
    m := make(map[string]Enum)
    for k, v := range enumToStr {
        m[v] = k
    }
    return m
}()

I first get the Type of the interface :

var stringeeType = reflect.TypeOf(new(Stringee)).Elem()

then I can perform checks against it:

e := EnumA
e.FromString("B")
e.String() // "B"


reflect.ValueOf(e).Type().Implements(stringeeType) // false
reflect.ValueOf(&e).Type().Implements(stringeeType) // true

Which is to be expected. However, what I don't know how to do is perform the check as a pointer if type is a field of a struct . For example:

Say I have the following struct :

type Struct struct {
    E Enum
}

How do I check to see whether or not E implements the interface Stringee ?

t := Struct{}
tv := reflect.ValueOf(&t)
fieldE := tv.Elem().Field(0)

fieldE.Type().Implements(stringeeType) // false

https://play.golang.org/p/eRUQ8EGo2-E

If the field is addressable, then take the address of the field and check that value:

if fieldE.CannAddr() { 
    impl := fieldE.Addr().Type().Implements(stringeeType)
    fmt.Println("&T.E implements Stringee", impl)
}

Alternatively if you have only the reflect.Type not the relect.Value , for example when checking if a struct's field implements an interface, you can use reflect.PtrTo for this.

This func will check if any field of the given struct implements Stringee :

func CheckFields(s interface{}) {
    rt := reflect.TypeOf(s)
    for i := 0; i < rt.NumField(); i++ {
        field := rt.Field(i)
        ptr := reflect.PtrTo(field.Type)
        if ptr.Implements(stringeeType) {
            fmt.Println(field.Name + " implements Stringee")
        }
    }
}

type Test struct {
    Field  int
    Field2 MyTypeWithStringee
}

test := Test{}

CheckFields(test)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM