1
votes

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.

2

2 Answers

5
votes

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

4
votes

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