zemirco
zemirco

Reputation: 16395

How to get parent embedded struct field value?

I have the following struct in my package.

type Animal struct {
  Id string
  // and some more common fields
}

A user who uses my package should be able to implement own Animals. As I need Id in one of my methods the user has to embed my struct.

type Dog struct {
  Animal
  Name string
  Legs int
}

In my package I have a save() function that saves Animals to database. As I don't know the user's type I have to use interface{} as argument type. My question is: How do I get the Id (from the parent struct Animal)? At the moment I use some JSON parsing and unmarshalling, but is this the way to go/Go?

func put(doc interface{}) error {
  res, err := json.Marshal(doc)
  if err != nil {
    return err
  }
  var animal *Animal
  err = json.Unmarshal(res, &animal)
  if err != nil {
    return err
  }
  fmt.Println(animal.Id) // finally I have the Id
  // ...
}

Usage

func main() {
  bello := Dog{
    Animal: Animal{
      Id: "abcd1234",
    },
    Name: "bello",
    Legs: 4,
  }
  err := put(bello)
  // ...
}

Upvotes: 3

Views: 4649

Answers (3)

Ainar-G
Ainar-G

Reputation: 36259

The way to go/Go here is to declare Animal as an interface:

type Animal interface {
    ID() int
    Name() string
    // Other Animal field getters here.
}

Then, save can take Animal as an argument and get all the info it needs using Animal's methods.

The other possbile way is to use the reflect package to obtain the Animal fields from the struct, but this will be buggier, dirtier and possibly slower that using an interface.

Upvotes: 2

VonC
VonC

Reputation: 1328652

Maybe you can add an interface in order to be sure to get a reference to the parent struct:

type AnimalGetter interface {
    GetAnimal() *Animal
}

func (dog *Dog) GetAnimal() *Animal {
    return &dog.Animal
}

That would allow your save method to do a type assertion (AnimalGetter):

func save(obj interface{}) {
    animal := obj.(AnimalGetter)
    fmt.Printf("%v\n", animal.GetAnimal())
}

See a complete example in play.golang.org.

Output:

&{{dogid} wouf 0}
&{dogid}

Simpler:

func save(animal AnimalGetter) {
    fmt.Printf("%v\n", animal.GetAnimal())
}

(play.golang.org)

Upvotes: 4

Not_a_Golfer
Not_a_Golfer

Reputation: 49265

You can do it with this small reflection trick, although perhaps using interfaces like @VonC suggested might be a more practical and idiomatic approach.

Anyway, here's the same achieved with reflection:

func main() {

    d := Dog {
        Animal { "id1111" },
        "Barky",
        4,
    }
    fmt.Println(reflect.ValueOf(d).FieldByName("Animal").Interface().(Animal))
}

Upvotes: 1

Related Questions