Nightwolf
Nightwolf

Reputation: 4697

How to modify a property value of a nested struct

I'm trying to modify the value of a nested struct's variable in Go. Basically, I want to modify the RsvpString property but GetRsvp() seems to return the value of Rsvp instead of a reference, so when I modify its property value, it doesn't get reflected in the Event instance.

The test is below.

type Event struct {
    Rsvps     []Rsvp `json:"rsvps"`
}

type Rsvp struct {
    UserId          string `json:"userId"`
    RsvpString      string `json:"rsvp"`
}

func (e *Event) GetRsvp(userId string) (rsvp *Rsvp, err error) {
    for _, element := range e.Rsvps {
        if element.UserId == userId {
            return &element, nil
        }
    }
    return &Rsvp{}, fmt.Errorf("could not find RSVP based on UserID")
}

func (e *Event) UpdateExistingRsvp(userId string, rsvpString string) {
    rsvp, err := e.GetRsvp(userId)
    if err == nil {
        rsvp.RsvpString = rsvpString
    }
}

Here's the test code:

func TestEvent_UpdateExistingRsvp(t *testing.T) {
    e := Event{[]Rsvp{
        {Name:      "Bill",
            UserId:    "bill",
            Rsvp:      "yes"}}}

    e.UpdateExistingRsvp("bill", "no")
    assert.Equal(t, "no", e.Rsvps[0].Rsvp, "RSVP should be switched to no") // fails
}

Upvotes: 0

Views: 1094

Answers (2)

Chethan Gowda
Chethan Gowda

Reputation: 9

When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.

so technically you are trying to modify the copy of the Rsvp. instead, return the index and from the GetRsvp() method and update.

func (e *Event) GetRsvp(userId string) (int, error) {
    for index , element := range e.Rsvps {
        if element.UserId == userId {
            return index, nil
        }
    }
    return -1 , fmt.Errorf("could not find RSVP based on UserID")
}

func (e *Event) UpdateExistingRsvp(userId string, rsvpString string) {
    index, err := e.GetRsvp(userId)

    if err != nil || index == -1  {
        fmt.Println("no such user")
    }
    e.Rsvps[index].RsvpString = rsvpString
}

Upvotes: 0

Burak Serdar
Burak Serdar

Reputation: 51577

GetRsvp is returning the address of the loop variable, not the address of the element in the array. To fix:

    for i, element := range e.Rsvps {
        if element.UserId == userId {
            return &e.Rsvps[i], nil
        }
    }

The loop variable keeps a copy of e.Rsvps[i], and it gets overwritten at every iteration. If you return the address of the loop variable, then you return the address of that copy.

Upvotes: 3

Related Questions