Quest
Quest

Reputation: 580

How to embed struct values via an embedded interface : Composable Structs

This question is best described by an example

http://play.golang.org/p/bQuRr0kV-b

I am trying to make a composable struct. In this example, I want to have a Person type with embedded values from either Female or Male. If I were just dealing with structs, I would embed them like this

type Person Struct{
  Female
  Male
}

I cannot do this however, both because in the actual project, there are a lot of embedded structs and I would prefer to keep the struct clean and composable. But there is also a naming conflict — in this example, both Male and Female contain the field 'Eyes'. Moving the conflicting value to the Person struct is not a viable solution (as many of the other embedded structs do not contain that particular value).

I was hoping to pass these values via a simple interface. Sample Below: When running this code, I get &{Name: Age:0 Type:male GenderType:0x10434120} where GenderType is the pointer to Male struct(in this case). My Goal is to have a flat structure returned that would look like &{Name: Age:0 Type:male Eyes: ChestSize:0}

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    Type string
    GenderType 
}

type GenderType interface {
    TypeName() string
}

type Male struct {
    Eyes     string
    ChestSize int
}

type Female struct {
    Eyes string
    BustSize int
}

func (m *Male) TypeName() string {
    return "male"
}

func (f *Female) TypeName() string {
    return "female"
}

func main() {

    d := NewHuman(new(Male))
    fmt.Printf("%+v", d)
}

func NewHuman(gt GenderType) *Person {
    return &Person{
        Type: gt.TypeName(),
        GenderType: gt,

    }
}

Upvotes: 2

Views: 726

Answers (1)

Corvus Crypto
Corvus Crypto

Reputation: 2291

I don't think it is possible to do this in a flat structure because it would entail changing the memory structure of the struct type during runtime, which go doesn't allow. While you can access embedded fields using GenderType, it's still allowed because you are saying that this will be a reference to an embedded struct that satisfies the interface rather than changing the structure of the struct itself.

I think the better way to marshal into a flat json using this is to keep your embedded structure, but then make the Person struct a Marshaler

You can add your own custom MarshalJSON() (byte[],error) method and use this to make a flat json output.

If you need specialized unmarshaling, then you can do likewise with an (UnmarshalJSON([]byte) error) method tied to Person.

see https://golang.org/pkg/encoding/json/#Marshaler for further reference

Here is a playground that shows what I mean: https://play.golang.org/p/qOl9WSaI3O

Upvotes: 2

Related Questions