一尘不染

在for循环中使用指针

go

我正在努力理解为什么我的代码处于一种状态而不是另一种状态。自从我讲完指针已经有一段时间了,所以我可能会生锈!

基本上,我有一个用于将对象存储在内存中的具有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
}

上面的方法不起作用,看起来一开始一切都很好,但是稍后尝试访问数据时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)
}

我假设的问题是,由于循环覆盖参考chartfor循环中,指针引用也会改变。在独立函数中生成指针时,该引用永远不会被覆盖。那正确吗?

我觉得自己很愚蠢,但是指针不应该由生成&chart并且与chart引用无关吗?我也尝试创建的指针一个新的变量p := &chartfor循环里,没有任何工作。

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


阅读 1757

收藏
2020-07-02

共1个答案

一尘不染

这是因为只有一个循环变量chart,并且在每次迭代中都只为其分配了一个新值。因此,如果您尝试获取循环变量的地址,则在每次迭代中它都将是相同的,因此您将存储相同的指针,并且在每次迭代中(以及在循环之后,它都将覆盖)指向对象(循环变量)将保留在上一次迭代中分配的值)。

规范中对此进行了提及:对于语句:对于带有range子句的语句:

可以使用简短变量声明:=)的形式由“
range”子句声明迭代变量。在这种情况下,将它们的类型设置为各个迭代值的类型,并且它们的范围是“
for”语句的块; 它们在每次迭代中都会重复使用 。如果迭代变量在“ for”语句之外声明,则执行后它们的值将是上一次迭代的值。

您的第二个版本有效,因为您将循环变量传递给了一个函数,因此将对其进行复制,然后存储该副本的地址(与循环变量分离)。

您可以在没有功能的情况下达到相同的效果:只需创建一个本地副本并使用该地址即可:

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

另请注意,您还可以存储slice元素的地址:

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

这样做的缺点是,由于您存储了指向slice元素的指针,因此只要保留了任何指针,就必须将slice的整个支持数组保留在内存中(无法对数组进行垃圾回收)。此外,您存储的指针将Chart与切片共享相同的值,因此,如果有人修改传递的切片的图表值,则会影响您存储其指针的图表。

2020-07-02