Home > Software design >  Passing struct array with anonymous field in Go
Passing struct array with anonymous field in Go

Time:01-20

this question is related to Passing struct with anonymous field in Go but different in that I am dealing with a function that handles an array of Parent items rather than just one struct, ie:

package main

import "fmt"

type Parent struct {
    Dad string
}

type Child struct {
    Parent
    Son string
}

func myfunc(data []Parent) {
    for n, _ := range data {
        fmt.Printf("Dad is %s\n", data[n].Dad)
    }
}

func main() {
    data := make([]Child, 2)
    data[0].Dad = "pappy"
    data[0].Son = "sonny"
    data[1].Dad = "daddy"
    data[1].Son = "billy"
    myfunc(data)
}

I tried to cast data to an array of Parents like myfunc([]Parent(data)) but that didnt work and obviously the solution of myfunc(data.Parent) wont work on an array.

Is there a reasonable solution to this in golang without resorting to creating a new slice or generics which are not out of beta yet?

Cheers

CodePudding user response:

You cannot do this even with generics. data[n].Dad will not work.

The reasonable way to deal with it is to use an interface, but still, you have to create an interface slice for it:

type WithDad interface {
   GetDad() string
}

func (p Parent) GetDad() string {return p.Dad}

func myFunc(data []WithDad) {
   ...
}

...
arr:=make([]WithDad,0,len(data))
for _,x:=range data {
  arr=append(arr,x)
}
myFunc(arr)

The reason for this is how the type system works and how slices are passed around. The slice []Child points to an array where each entry is a Child. A function that takes []Parent expects a slice whose elements are Parent, which is a different type, so you cannot pass one for the other.

The slice []WithDad points to an array where each entry is an interface. Each such entry points to the Child or Parent entry in another slice.

CodePudding user response:

Just for completeness I made a complete version of Burak Serdar code I got working (I like it when full working examples are posted for me to cut and paste later :P) ... note I deliberately return a pointer to the Parent because in practice its likely you will want to work on that complete struct and not just read 1 field.

package main

import "fmt"

type WithDad interface {
    GetDad() *Parent
}

type Parent struct {
    Dad string
}

func (p *Parent) GetDad() *Parent {
    return p
}

type Child struct {
    Parent
    Son string
}

func (c *Child) GetDad() *Parent {
    return &c.Parent
}

func myfunc(data []WithDad) {
    for n, _ := range data {
        fmt.Printf("Dad is %s\n", data[n].GetDad().Dad)
    }
}

func main() {
    data := make([]WithDad, 2)
    data[0] = &Child{Parent: Parent{Dad: "pappy"}, Son: "sonny"}
    data[1] = &Child{Parent: Parent{Dad: "daddy"}, Son: "billy"}
    myfunc(data)
}

CodePudding user response:

Your structure with Child being a composite of Parent and a string (which I guess contains the name) makes little sense. A child is not a parent plus their name. In fact you only have one concept, namely Person, who potentially has a parent. Please consider the following approach:

package main

import "fmt"

type Person struct {
   Name string
   Parent *Person
}

func myfunc(data []Person) {
    for n, _ := range data {
        fmt.PrintLn("My name is", n.Name)
        if n.Parent != nil {
            fmt.PrintLn("My dad is", n.Parent.Name)
        }
    }
}

func main() {
    data := make([]Person, 2)
    data[0].Name = "daddy"
    data[1].Name = "billy"
    data[1].Parent = &data[0]
    myfunc(data)
}
  •  Tags:  
  • Related