Home > Net >  golang how to access promoted type
golang how to access promoted type

Time:02-01

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?

playground example

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)
  •  Tags:  
  • Related