我很难理解为什么我在一个州的代码中有bug,而在另一个州却没有.我已经有一段时间没有讨论过指针了,所以我可能已经生疏了!

基本上,我有一个存储库 struct ,用来在内存中存储一个对象,它有一个Store函数.

type chartsRepository struct {
    mtx    sync.RWMutex
    charts map[ChartName]*Chart
}

func (r *chartsRepository) Store(c *Chart) error {
    r.mtx.Lock()
    defer r.mtx.Unlock()
    r.charts[c.Name] = c
    return nil
}

因此,它所做的就是设置一个RW互斥锁,并将指针添加到一个由标识符引用的映射.

然后,我得到了一个函数,它基本上循环遍历这些对象的一部分,将它们全部存储在存储库中.

type service struct {
    charts Repository
}

func (svc *service) StoreCharts(arr []Chart) error {
    hasError := false
    for _, chart := range arr {
        err := svc.repo.Store(&chart)
        // ... error handling
    }
    if hasError {
        // ... Deals with the error object
        return me
    }
    return nil
}

上面的方法不起作用,起初看起来一切正常,但是在稍后try 访问数据时,映射中的所有条目都指向相同的Chart对象,尽管具有不同的键.

如果我执行以下操作并将指针引用移动到另一个函数,则一切都按预期进行:

func (svc *service) StoreCharts(arr []Chart) error {
    // ...
    for _, chart := range arr {
        err := svc.storeChart(chart)
    }
    // ...
}

func (svc *service) storeChart(c Chart) error {
    return svc.charts.Store(&c)
}

我假设问题是,因为循环覆盖了for循环中对chart的引用,指针引用也会改变.当指针在独立函数中生成时,该引用永远不会被覆盖.是这样吗?

我觉得自己很愚蠢,但是指针不应该是由&chart生成的吗?这不是独立于chart引用的吗?我还try 为for循环中的指针p := &chart创建一个新变量,但也没有成功.

我应该避免在循环中生成指针吗?

推荐答案

这是因为只有一个循环变量chart,并且在每次迭代中只给它分配了一个新值.因此,如果您try 获取循环变量的地址,它在每次迭代中都是相同的,因此您将存储相同的指针,并且指向的对象(循环变量)将在每次迭代中被覆盖(在循环之后,它将保存在最后一次迭代中分配的值).

在第Spec: For statements: For statements with range clause:节提到了这一点

迭代变量可以由"range"子句以short variable declaration(:=)的形式声明.在这种情况下,它们的类型被设置为各自迭代值的类型,并且它们的scope是"for"语句的块;they are re-used in each iteration.如果迭代变量是在"for"语句之外声明的,那么在执行之后,它们的值将是上一次迭代的值.

您的第二个版本可以工作,因为您将循环变量传递给函数,因此将对其进行复制,然后存储副本的地址(与循环变量分离).

不过,您可以在不使用函数的情况下实现相同的效果:只需创建一个本地副本并使用该副本的地址:

for _, chart := range arr {
    chart2 := chart
    err := svc.repo.Store(&chart2) // Address of the local var
    // ... error handling
}

还请注意,还可以存储切片元素的地址:

for i := range arr {
    err := svc.repo.Store(&arr[i]) // Address of the slice element
    // ... error handling
}

这样做的缺点是,因为您存储指向片元素的指针,所以只要您保留任何指针(数组不能被垃圾收集),片的整个后备数组就必须保留在内存中.此外,您存储的指针将与切片共享相同的Chart个值,因此,如果有人修改传递的切片的图表值,将会影响您存储其指针的图表.

请参阅相关问题:

Golang: Register multiple routes using range for loop slices/map

Why do these two for loop variations give me different behavior?

Go相关问答推荐

Go:嵌入类型不能是类型参数""

golang.org/x/oauth2 oauth2.Config.Endpoint.TokenURL mock:缺少access_token

从文件读取字节,将其保存到 struct 体并修改值

如何为循环扫描的bufio scanner 设置超时?

Kafka golang 生产者在错误后更改分区计数

Go - 永远停止带有上下文的循环

使用goroutines在Golang中验证 struct

使用goqu无法获取响应

命令行参数在 Golang 程序中不正确地接受为参数

Go test "-run -" 标志执行测试更快

从给定顶点查找图形中所有闭合路径的算法

在两个单独的速率受限端点之间同步请求

使用 ssh 从私有 gilab 仓库导入一个 go 项目:未知修订

Go 中的 HTTP 请求验证中间件

函数超时和 goroutine 泄漏

Golang:隐式 struct 匹配

Golang泛型在用作 map 元素时不起作用

使用反射在 struct 内迭代切片 struct

从golang中的url加载图像

go mod tidy 错误消息:但是 go 1.16 会 Select