简体   繁体   中英

Passing struct array with anonymous field in Go

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

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.

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

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

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