【问题标题】:Slice element not updated in go切片元素未在 go 中更新
【发布时间】:2021-03-13 15:26:13
【问题描述】:

我有一个如下的账户结构:

type Account struct {
    Id       string
    Name     string
    Address  string
    City     string
    Email    string
    Phone    string
    Username string
    Password string
    IsActive bool
}

我还有两个函数:findupdatefind 函数从切片中找到某个元素并返回指向该元素的指针:

func find(accounts []Account, username string) *Account {
    for _, acc := range accounts {
        if acc.IsActive && acc.Username == username {
            return &acc
        }
    }
    return nil
}

更新函数从参数中更改某些字段:

func update(account *Account, name string) {
    account.Name = name
}

main 中,我尝试find 切片中的一个元素并将其存储到帐户指针。然后,我尝试update 下一行中的一个字段。这就是我的main 的样子:

func main() {
    accounts := []Account{{
        Id:       "1",
        Name:     "name",
        Address:  "address",
        City:     "city",
        Email:    "email@email.com",
        Phone:    "123456789",
        Username: "username",
        Password: "1234",
        IsActive: true,
    }}
    var acc *Account
    acc = find(accounts, "username")
    fmt.Printf("%v\n", accounts)
    fmt.Println(acc)

    update(acc, "updated")
    fmt.Printf("%v\n", accounts)
    fmt.Println(acc)
}

问题是当update调用时,切片中的元素没有更新。它只是更新存储帐户的数据。下面是 main 的输出:

// find
[{1 name address city email@email.com 123456789 username 1234 true}]
&{1 name address city email@email.com 123456789 username 1234 true}

// update
[{1 name address city email@email.com 123456789 username 1234 true}]
&{1 updated address city email@email.com 123456789 username 1234 true}

当store元素发生变化时,如何让切片内的元素更新?感谢您的帮助,任何建议都将不胜感激。

【问题讨论】:

标签: go pointers slice


【解决方案1】:

在您的代码中,

func find(accounts []Account, username string) *Account {
    for _, acc := range accounts {
        if acc.IsActive && acc.Username == username {
            return &acc
        }
    }
    return nil
}

因为您正在迭代的切片是[]Account 类型,所以您获得的每个acc 值都是存储在切片中的值的副本。 因此,您引用的 acc 值不是存储在切片中的值。

要防止这种行为,请使用指针切片,即:[]*Account

避免引用切片索引,如您接受的答案中所示,您可能会遇到微妙之处,再次使用[]*Account

https://play.golang.org/p/ru_0v_z8E9l

package main

import (
    "fmt"
)

type Account struct {
    Id       string
    Name     string
    Address  string
    City     string
    Email    string
    Phone    string
    Username string
    Password string
    IsActive bool
}

func main() {
    accounts := []Account{{
        Id:       "1",
        Name:     "name",
        Address:  "address",
        City:     "city",
        Email:    "email@email.com",
        Phone:    "123456789",
        Username: "username",
        Password: "1234",
        IsActive: true,
    }}
    var acc *Account
    acc = find(accounts, "username")
    fmt.Println("the original data")
    fmt.Printf("%#v\n", accounts)
    fmt.Printf("%#v\n", acc)

    fmt.Println()
    fmt.Println("lets update the value pointed at the first position in the slice")
    update(acc, "updated")
    fmt.Printf("%#v\n", accounts)
    fmt.Printf("%#v\n", acc)
    fmt.Println("the item is updated in both variable, we are god so far.")

    fmt.Println()
    fmt.Println("increasing the length of the slice to force a backing array update")
    accounts = append(accounts, Account{})
    accounts = append(accounts, Account{})
    accounts = append(accounts, Account{}) // ensure append function changes the backing array

    fmt.Println()
    fmt.Println("now lets update that f pointer again")
    update(acc, "updated again") // update again
    fmt.Printf("%#v\n", accounts)
    fmt.Printf("%#v\n", acc)
    fmt.Println("Have you spotted the issue in the Account slice at position 0 ?")
    fmt.Println()
    fmt.Println(`In regard to OP question: How to make the element inside the slice updated when the store element changed?`)
    fmt.Println("it is a use case that fails")
}
func find(accounts []Account, username string) *Account {
    for i, acc := range accounts {
        if acc.IsActive && acc.Username == username {
            return &accounts[i]
        }
    }
    return nil
}
func update(account *Account, name string) {
    account.Name = name
}

【讨论】:

    【解决方案2】:

    问题是您返回的是元素的副本,而不是数组中元素的实际地址

    return &acc
    

    如下更改您的查找功能

    func find(accounts []Account, username string) *Account {
    for idx , acc := range accounts {
        if acc.IsActive && acc.Username == username {
            return &accounts[idx]
        }
    }
    return nil
    }
    

    您将得到预期的响应

    [{1 name address city email@email.com 123456789 username 1234 true}]
    &{1 name address city email@email.com 123456789 username 1234 true}
    [{1 updated address city email@email.com 123456789 username 1234 true}]
    &{1 updated address city email@email.com 123456789 username 1234 true}
    

    您可以查看更新后的代码here in go playground

    【讨论】:

    • 请考虑,如果切片支持数组已更新并且您继续尝试更新指针,则此代码将遇到奇怪的行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 2019-12-22
    • 2019-01-02
    相关资源
    最近更新 更多