I have a 'common' structure promoted within two specific structures. For example:
type common struct {
name string
}
type apple struct {
common
}
type orange struct {
common
}
Details specific to apple and orange are omitted.
I have a type-specific map of each, e.g., map[string]*apple and map[string]*orange.
I am trying to make a single function that can extract the common pointers. From what I've tried so far, reflection appears required.
My function is:
func getFruitArray(theMap interface{}) []*common {
m := reflect.ValueOf(theMap)
cf := make([]*common, 0, m.Len())
for _, mk := range m.MapKeys() {
v := m.MapIndex(mk)
cf = append(cf, v.Interface().(*common))
}
return cf
}
This function fails at cf = append(cf, v.Interface().(*common)) with:
panic: interface conversion: interface {} is *main.apple, not *main.common
Is there a way to access the promoted struct common without specifically referencing apple or orange in this function?
CodePudding user response:
See Burak's answer which makes the reasonable compromise of having to call a method to receive the value.
Regardless, below is a solution which uses reflection as you planned. Note that common needs to be Common (exported field) else the reflect package cannot read it.
package main
import (
"log"
"reflect"
)
type Common struct {
name string
}
type apple struct {
Common
}
type orange struct {
Common
}
func getFruitArray(theMap interface{}) []*Common {
m := reflect.ValueOf(theMap)
cf := make([]*Common, 0, m.Len())
for _, mk := range m.MapKeys() {
v := m.MapIndex(mk)
f := v.Elem().FieldByName("Common")
cf = append(cf, f.Addr().Interface().(*Common))
}
return cf
}
func main() {
appleMap := make(map[string]*apple)
orangeMap := make(map[string]*orange)
a1 := &apple{}
a1.name = "my apple"
appleMap["test"] = a1
o1 := &orange{}
o1.name = "my orange"
orangeMap["test2"] = o1
f1 := getFruitArray(appleMap)
for _, c := range f1 {
log.Printf("f1: %s", c.name)
}
f2 := getFruitArray(orangeMap)
for _, c := range f2 {
log.Printf("f2: %s", c.name)
}
}
https://go.dev/play/p/FrkRnu_G2Xd
CodePudding user response:
You don't need reflection. One way is to use an interface:
type common struct {
name string
tag string
}
func (c *common) GetCommon() *common {return c}
type WithCommon interface {
GetCommon() *common
}
Then you can do:
func getFruitArray(theMap map[string]WithCommon) []*common {
cf := make([]*common, 0, theMap.Len())
for _,k:=range theMap {
cf=append(cf,k.GetCommon())
}
return cf
}
But you also have to do:
appleMap := make(map[string]WithCommon)
orangeMap := make(map[string]WithCommon)
