【问题标题】:How to modify a property value of a nested struct如何修改嵌套结构的属性值
【发布时间】:2019-11-21 04:39:05
【问题描述】:

我正在尝试修改 Go 中嵌套结构变量的值。基本上,我想修改RsvpString 属性,但GetRsvp() 似乎返回Rsvp 的值而不是引用,所以当我修改它的属性值时,它不会反映在Event 实例中。

测试如下。

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

这是测试代码:

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
}

【问题讨论】:

    标签: pointers go struct


    【解决方案1】:

    GetRsvp 正在返回循环变量的地址,而不是数组中元素的地址。修复:

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

    循环变量保留一份 e.Rsvps[i] 的副本,并在每次迭代时被覆盖。如果返回循环变量的地址,则返回该副本的地址。

    【讨论】:

    • 谢谢,这完全有道理。我没有意识到循环变量会复制。
    • 顺便说一句,我发现对于这样的情况,我更喜欢(风格上)只使用for i := range e.Rsvps {(如果测试变得复杂,然后将本地指针绑定到&e.Rsvps[i])。
    【解决方案2】:

    在切片上进行测距时,每次迭代都会返回两个值。第一个是索引,第二个是该索引处元素的副本。

    所以从技术上讲,您正在尝试修改 Rsvp 的副本。 相反,从 GetRsvp() 方法返回索引并进行更新。

    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
    }
    

    【讨论】:

    • 谢谢!这也有效,但我想远离使用索引作为返回值。由于这种差异,我接受了 Burak 的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多