我在一个正在研究的项目中遇到了一个问题。我找到了解决方法,但我不确定为什么我的解决方案有效。我希望有人更多地体验Go指针的工作方式可以帮助我。
我有一个Model接口和一个实现接口的Region结构。 Model接口在Region结构的指针上实现。我还有一个Regions集合,它是Region对象的一部分。我有一个方法可以将Regions对象转换为[]模型:
// Regions is the collection of the Region model
type Regions []Region
// Returns the model collection as a list of models
func (coll *Regions) ToModelList() []Model {
output := make([]Model, len(*coll))
for idx, item := range *coll {
output[idx] = &item
}
return output
}
当我运行这段代码时,我最终得到了多次输出Region的第一个指针。因此,如果Regions集合有两个不同的项目,我将获得相同的地址重复两次。当我在切片中设置变量之前打印变量时,它们会有正确的数据。
我搞砸了一下,以为Go可能会重复使用循环之间的内存地址。这个解决方案目前在我的测试中适用于我:
// Returns the model collection as a list of models
func (coll *Regions) ToModelList() []Model {
output := make([]Model, len(*coll))
for idx, _ := range *coll {
i := (*coll)[idx]
output[idx] = &i
}
return output
}
这给出了输出切片中两个不同地址的预期输出。
老实说这似乎是范围函数在运行之间重用相同内存地址的错误,但我总是假设我在这种情况下缺少某些东西。
我希望我能为你解释得这么好。我很惊讶原始解决方案不起作用。
谢谢!
答案 0 :(得分:6)
在您的第一个(非工作)示例item
中是循环变量。它的地址不变,只有它的价值。这就是你在输出idx
次获得相同地址的原因。
运行此代码以查看正在运行的机制;
func main() {
coll := []int{5, 10, 15}
for i, v := range coll {
fmt.Printf("This one is always the same; %v\n", &v)
fmt.Println("This one is 4 bytes larger each iteration; %v\n", &coll[i])
}
}
答案 1 :(得分:2)
整个循环只有一个item
变量,在循环的每次迭代过程中都会赋予相应的值。您在每次迭代中都没有获得新的item
变量。所以你只是反复获取同一个变量的地址,这当然是相同的。
另一方面,如果你在循环中声明了一个局部变量,它将在每次迭代中成为一个新变量,并且地址将是不同的:
for idx, item := range *coll {
temp := item
output[idx] = &temp
}