How to get parent embedded struct field value? - go

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

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)

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

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.

Related

Golang update struct field inside function passed as interface

I am new to go (golang). That is why my question may be irrelevant (or impossible to answer).
I have created two structs. Both of these embed another struct. Now I want to update a field of the embedded struct inside a function.
package main
import (
"fmt"
"reflect"
"time"
)
type Model struct {
UpdatedAt time.Time
}
type Fruit struct {
Model
label string
}
type Animal struct {
Model
label string
}
func update(v interface{}) {
reflectType := reflect.TypeOf(v)
reflectKind := reflectType.Kind()
if reflectKind == reflect.Ptr {
reflectType = reflectType.Elem()
}
m := reflect.Zero(reflectType)
fmt.Println(m)
}
func main() {
apple := &Fruit{
label: "Apple",
}
tiger := &Animal{
label: "Tiger",
}
update(apple)
update(tiger)
fmt.Println(apple)
fmt.Println(tiger)
}
I wish to implement the update function so that it will put the current time in UpdatedAt of the passed struct. But I am not able to do this.
In this case, the field of Fruit and Animal is same: label. But it will not always be. Please keep that in mind when providing your suggestions.
Any guidance would be much appreciated.
I'd avoid reflect or interface{} if you're starting to learn go. Beginners usually fall back on them like a void * crutch. Try to use concrete types or well defined interfaces.
This should get you going:
type Timestamper interface {
Update()
UpdatedTime() time.Time
}
type Model struct {
updated time.Time
}
func (m *Model) Update() { m.updated = time.Now() }
func (m *Model) UpdatedTime() time.Time { return m.updated }
type Fruit struct {
Model
label string
}
type Animal struct {
Model
label string
}
// update will work with a `Model` `Animal` or `Fruit`
// as they all implement the `Timestamper` interface`
func update(v Timestamper) {
v.Update()
}
Playground: https://play.golang.org/p/27yDVLr-zqd
Assuming you want to achieve this via reflection: first of all, you have to pass a pointer to the struct. You're now passing a copy of the struct, so any modifications done in update will be done on the copy, not on the instance you passed in. Then, you can lookup the field UpdatedAt in the interface passed in, and set it.
That said, that's probably not the best way to do this. Another way of doing this without reflection is:
func update(in *Model) {
in.UpdatedAt = time.Now()
}
func main() {
apple := &Fruit{}
update(&apple.Model)
}
Or:
func (in *Model) update() {
in.UpdatedAt = time.Now()
}
func main() {
apple := &Fruit{}
apple.update()
}

Is there a more idiomatic approach to creating variables of specific types based on input?

In this playground link I have created a contrived version of my code where I am creating a variable of type X based on an input string. The variable will be one of a handful of types and implement an interface.
The code currently compiles and provides the correct result, however it strikes me as quite verbose and I'm trying to find if there is a shorthand approach to the result I'm achieving. The example has 3 types (dog, cat & bird) that implement the interface (animal), however my actual code will have up to 40 types in this switch statement.
The reason I am using this code is when retrieving results form a DBMS, I'm trying to use a generic load method that, when combined with sqlx, loads the database table into the correct struct based on the input string. I have entire control over the application and can change the input string to another type if required.
Code from playground link:
package main
import (
"fmt"
)
type animal interface {
call() string
}
type dog struct {
}
func (d *dog) call() string {
return "Woof!"
}
type cat struct {
}
func (c *cat) call() string {
return "Meow!"
}
type bird struct {
}
func (c *bird) call() string {
return "Chirp!"
}
func main() {
var animal animal
animalType := "dog"
switch animalType{
case "dog":
animal = new(dog)
case "cat":
animal = new(cat)
case "bird":
animal = new(bird)
You can create a hashmap from "string" to "function that returns animal" but setting that up would be more verbose than the switch statement.
Something like this (not tested)
type AnimalCtor func() animal
var animalMap map[string]AnimalCtor
.....
func init() {
animalMap["dog"] = func() animal { return &dog{} }
animalMap["cat"] = func() animal { return &cat{} }
animalMap["bird"] = func() animal { return &bird{} }
.....
}
func createAnimalFromString(input string) animal {
ctor, ok := animalMap[input]
if ok {
return ctor()
} else {
return nil
}
}
But it's a lot more verbose than the switch statement and obscures what should otherwise be explicit and clear.

Typecasting one function into another in golang

I have the following code:
package vault
type Client interface {
GetHealth() error
}
func (c DefaultClient) GetHealth () error {
resp := &VaultHealthResponse{}
err := c.get(resp, "/v1/sys/health")
if err != nil {
return err
}
return nil;
}
Now, I want to use this function as part of this struct:
type DependencyHealthFunction func() error
type Dependency struct {
Name string `json:"name"`
Required bool `json:"required"`
Healthy bool `json:"healthy"`
Error error `json:"error,omitempty"`
HealthFunction DependencyHealthFunction
}
Basically, set the value of HealthFunction to GetHealth. Now, when I do the following:
func (config *Config) GetDependencies() *health.Dependency {
vaultDependency := health.Dependency{
Name: "Vault",
Required: true,
Healthy: true,
HealthFunction: vault.Client.GetHealth,
}
temp1 := &vaultDependency
return temp1;
}
This gives me an error and it says cannot use vault.Client.GetHealth (type func(vault.Client) error) as type health.DependencyHealthFunction in field value. How can I do this?
Edit: How DependencyHealthFunction is used?
As its part of Dependency struct, it's simply used as following: d.HealthFunction() where d is a variable of type *Dependency.
This is abstract:
HealthFunction: vault.Client.GetHealth,
If we were to call HealthFunction(), what code do you expect to run? vault.Client.GetHealth is just a promise that such a function exists; it isn't a function itself. Client is just an interface.
You need to create something that conforms to Client and pass its GetHealth. For example, if you had a existing DefaultClient such as:
defaultClient := DefaultClient{}
Then you could pass its function:
HealthFunction: defaultClient.GetHealth,
Now when you later call HealthFunction() it will be the same as calling defaultClient.GetHealth().
https://play.golang.org/p/9Lw7uc0GaE
I believe the issue is related to understanding how interfaces are treated in Go.
An interface simply defines a method or set of methods that a particular type must satisfy to be considered as "implementing" the interface.
For example:
import "fmt"
type Greeter interface {
SayHello() string
}
type EnglishGreeter struct{}
// Satisfaction of SayHello method
func (eg *EnglishGreeter) SayHello() string {
return "Hello"
}
type SpanishGreeter struct{}
func (sg *SpanishGreeter) SayHello() string {
return "Ola"
}
func GreetPerson(g Greeter) {
fmt.Println(g.SayHello())
}
func main() {
eg := &EnglishGreeter{}
sg := &SpanishGreeter{}
// greet person in english
GreetPerson(eg)
// greet person in spanish
GreetPerson(sg)
}
You can add this behavior into a custom struct by simply having a Greeter field inside the struct. ie
type FrontEntrance struct {
EntranceGreeter Greeter
}
fe := &FrontEntrance { EntranceGreeter: &EnglishGreeter{} }
// then call the SayHello() method like this
fe.EntranceGreeter.SayHello()
Interfaces in golang are useful for composing common expected behavior for types based on the methods that they satisfy.
Hope this helps.

Underlying pointer type from interface value

How do I get the underlying pointer type from an interface?
package main
import (
"fmt"
)
type Car interface {
Drive() string
}
type MyCar struct {
name string
}
func (MyCar) Drive ( ) string {
return "rum rum"
}
func main() {
var car Car
mycar := &MyCar{name:"mycar"}
car = mycar
mycarptr, err := car.(*MyCar)
mycarvalue, err2 := car.(MyCar)
fmt.Printf( "as ptr failed: %t, as value failed: %t\n", err, err2 )
fmt.Printf( "as ptr: %+v, as value: %+v", mycarptr, mycarvalue)
}
Your first assertion to *MyCar works fine
Here is a playground example to illustrate
Your second assertion to MyCar will fail since it's not a pointer.
To be able to modify the car you need to use a pointer to it (like you already did), however to make it more clear to others (and yourself) you should define the interface method on the pointer:
type Drivable interface {
Drive() string
}
type Car struct {
name string
}
func (*Car) Drive() string {
return "rum rum"
}
type SpaceShip struct {
name string
}
func (*SpaceShip) Drive() string {
return "sound spaceships makes when you drive / fly them"
}
func Drive(d Drivable) {
switch d := d.(type) { // d now is the actual type
case *Car:
fmt.Println("Got a car named", d.name)
case *SpaceShip:
fmt.Println("Got a spaceship named", d.name)
}
}
playground
I recommend going through Effective Go, and pay extra attention to the Interfaces And Methods section.

Golang polymorphic parameters and returns

Say I have functions:
func ToModelList(cats *[]*Cat) *[]*CatModel {
list := *cats
newModelList := []*CatModel{}
for i := range list {
obj := obj[i]
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
func ToModelList(dogs *[]*Dog) *[]*DogModel {
list := *dogs
newModelList := []*DogModel{}
for i := range list {
obj := obj[i]
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
Is there a way to combine those two so I can do something like
func ToModelList(objs *[]*interface{}) *[]*interface{} {
list := *objs
// figure out what type struct type objs/list are
newModelList := []*interface{}
// type cast newModelList to the correct array struct type
for i := range list {
obj := obj[i]
// type cast obj based on objs's type
newModelList = append(newModelList, obj.ToModel())
}
return &newModelList
}
First, slices are already a reference, unless you need to change the slice itself, you do not need to pass it as a pointer.
Second, an interface{} can be regardless an object or a pointer to an object. You do not need to have *interface{}.
I am not sure what you are trying to achieve but you could do something like this:
package main
// Interface for Cat, Dog
type Object interface {
ToModel() Model
}
// Interface for CatModel, DogModel
type Model interface {
Name() string
}
type Cat struct {
name string
}
func (c *Cat) ToModel() Model {
return &CatModel{
cat: c,
}
}
type CatModel struct {
cat *Cat
}
func (c *CatModel) Name() string {
return c.cat.name
}
type Dog struct {
name string
}
func (d *Dog) ToModel() Model {
return &DogModel{
dog: d,
}
}
type DogModel struct {
dog *Dog
}
func (d *DogModel) Name() string {
return d.dog.name
}
func ToModelList(objs []Object) []Model {
newModelList := []Model{}
for _, obj := range objs {
newModelList = append(newModelList, obj.ToModel())
}
return newModelList
}
func main() {
cats := []Object{
&Cat{name: "felix"},
&Cat{name: "leo"},
&Dog{name: "octave"},
}
modelList := ToModelList(cats)
for _, model := range modelList {
println(model.Name())
}
}
You define interfaces for your Cat, Dogs etc and for your Model. Then you implement them as you want and it is pretty straight forward to do ToModelList().
you can make *CatModel and *DogModel both implement type PetModel {} interface, and just return []Pet in function signature.
func (cats []*Cat) []PetModel {
...
return []*CatModel {...}
}
func (dogs []*Dog) []PetModel {
...
return []*DogModel {...}
}
BTW: return a pointer of a slice in golang is useless.
If you strip away redundant assignments, and unnecessary pointers-to-slices, you'll find you have little code left, and duplicating it for each of your model types doesn't look so bad.
func CatsToCatModels(cats []*Cat) []*CatModel {
var result []*CatModel
for _, cat := range cats {
result = append(result, cat.ToModel())
}
return result
}
Unless this code is used in a lot of places I'd also consider just inlining it, since it's trivial code and only 4 lines when inlined.
Yes, you can replace all the types with interface{} and make the code generic, but I don't think it's a good tradeoff here.

Resources