我是golang的新手,并且很难理解为什么我的代码中有一个错误而不是另一个状态。我已经有一段时间了,但是我可能已经生锈了!
基本上我有一个存储库结构,用于在内存中存储一个具有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)
}
我假设问题是因为循环覆盖chart
循环中对for
的引用,指针引用也会改变。当指针在独立函数中生成时,该引用永远不会被覆盖。是吗?
我觉得自己很愚蠢,但是&chart
不应该生成指针,而且它与chart
引用无关?我还尝试在p := &chart
循环中为指针for
创建一个新变量,但这也不起作用。
我应该避免在循环中生成指针吗?
对于众多问题感到抱歉,但我真的很想解决这个问题,我似乎无法找到解释清楚的资源。
感谢。
答案 0 :(得分:17)
这是因为只有一个循环变量chart
,并且在每次迭代中只为其分配一个新值。因此,如果您尝试获取循环变量的地址,它将在每次迭代中相同,因此您将存储相同的指针,并且在每次迭代中覆盖指向的对象(循环变量)(并且在循环之后它将保留最后一次迭代中指定的值。)
Spec: For statements: For statements with range
clause:
迭代变量可以使用short variable declaration(
:=
)的形式由“range”子句声明。在这种情况下,它们的类型设置为相应迭代值的类型,它们的scope是“for”语句的块; 在每次迭代中重复使用。如果迭代变量在“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
}
这样做的缺点是,由于存储了指向slice元素的指针,因此只要保留任何指针(数组不能被垃圾收集),就必须将切片的整个后备数组保留在内存中。 。此外,您存储的指针将与切片共享相同的Chart
值,因此如果有人修改传递切片的图表值,则会影响您存储指针的图表。
参见相关问题:
Golang: Register multiple routes using range for loop slices/map
Why do these two for loop variations give me different behavior?
答案 1 :(得分:0)
我今天遇到了类似的问题,创建这个简单的示例有助于我理解问题。
// Input array of string values
inputList := []string {"1", "2", "3"}
// instantiate empty list
outputList := make([]*string, 0)
for _, value := range inputList {
// print memory address on each iteration
fmt.Printf("address of %v: %v\n", value, &value)
outputList = append(outputList, &value)
}
// show memory address of all variables
fmt.Printf("%v", outputList)
此打印输出:
address of 1: 0xc00008e1e0
address of 2: 0xc00008e1e0
address of 3: 0xc00008e1e0
[0xc00008e1e0 0xc00008e1e0 0xc00008e1e0]
如您所见,即使实际值不同(“ 1”,“ 2”和“ 3”),每次迭代中value
的地址也始终相同。这是因为value
被重新分配了。
最后,outputList
中的每个值都指向相同的地址,该地址现在存储值“ 3”。