在处理Go中带有引用的切片时是否存在错误?

在处理Go中带有引用的切片时是否存在错误?,go,Go,我试图构建一个新的结构列表,其中包含对另一个切片中存在的项的引用。如果您看到它,就更容易理解,因此我准备了一个可以运行的代码片段。 我有一个包含两个点(笛卡尔坐标)的列表(dummylist),我想对其进行解析,以构建一个新的列表(mylist),其中的项目具有一些特性(在示例中,X>80)。我定义了两点:{X:90.0,Y:50.0}和{X:20.0,Y:30.0}。我希望mylist将包含{X:90.0,Y:50.0},而在末尾是{X:20.0,Y:30.0}。通过这里和那里的一些打印,我可

我试图构建一个新的结构列表,其中包含对另一个切片中存在的项的引用。如果您看到它,就更容易理解,因此我准备了一个可以运行的代码片段。 我有一个包含两个点(笛卡尔坐标)的列表(
dummylist
),我想对其进行解析,以构建一个新的列表(
mylist
),其中的项目具有一些特性(在示例中,X>80)。我定义了两点:{X:90.0,Y:50.0}和{X:20.0,Y:30.0}。我希望mylist将包含{X:90.0,Y:50.0},而在末尾是{X:20.0,Y:30.0}。通过这里和那里的一些打印,我可以验证算法是否工作正常(在正确的情况下,它以“if”条件输入),但最后,“mylist”包含错误的元素

package main
import(
    "fmt"
)

func main() {

type point struct {
    X float64
    Y float64
}

type pointsList []point

type pointContainer struct {
    Point *point
}

type pointContainerList []pointContainer

// Prepare a slice with two elements
dummylist := new(pointsList)
*dummylist = append(*dummylist, point{X:90.0, Y:50.0})
*dummylist = append(*dummylist, point{X:20.0 , Y:30.0})

// My empty list
mylist := new(pointContainerList)

fmt.Println(fmt.Sprintf("---- At the beginning, mylist contains %d points", len(*mylist)))

// Filter the initial list to take only elements
for _, pt := range *dummylist {
    fmt.Println("\n---- Evaluating point ", pt)

    if pt.X > 80 {
        fmt.Println("Appending", pt)
        *mylist = append(*mylist, pointContainer{Point: &pt})
        fmt.Println("Inserted point:", (*mylist)[0].Point, "len = ", len(*mylist))
    }
}

// mylist should contain {X:90.0, Y:50.0}, instead...
fmt.Println(fmt.Sprintf("\n---- At the end, mylist contains %d points", len(*mylist)))
fmt.Println("Content of mylist:", (*mylist)[0].Point)
}
您可以在此处运行代码:

一些有益的考虑: 我已经通过多次测试了解到,在最后,mylist包含循环中最后一个解析的项。我认为推荐信有问题。这就像列表中插入的项目(在第一次迭代中)依赖于其他迭代的“pt”。相反,如果我对I使用索引(
,pt:=range*dummylist
(*dummylist)[I]
),一切都正常


在谈论Golang的虫子之前。。。我遗漏了什么吗?

是的,你遗漏了什么。在这一行:

*mylist = append(*mylist, pointContainer{Point: &pt})
您正在将循环变量
&pt
的地址放入结构中。随着循环的继续,
pt
的值会发生变化。(或者换句话说,
&pt
将是循环每次迭代的相同指针)

从:

迭代值被分配给相应的迭代 赋值语句中的变量

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


一种解决方案是创建一个新的值,但我不确定您从这么多指针中获得了什么:
[]点
可能比指向
的指针的一片结构的指针更有效(也更不容易出错)。

问题的原因与此问题相同:(答案在注释中)简而言之,
pt
在每次迭代中都会被重用,因此表达式
&pt
的计算结果总是相同的,即指向相同内存地址的相同指针,因此容器列表中的每个元素都是指向单个
pt
值的指针的副本,在循环结束时,将始终包含在…上迭代的切片/数组的最后一个元素。。。。只需在迭代块的顶部执行
pt:=pt
即可解决您的问题;它感觉太像C了,我想/希望所有这些新语言都能帮助取代它;-)除非您有充分的理由,因为这些考虑超出了问题的范围,否则我不会使用指针编写此代码,因为别名问题可能会再次困扰您。这是我的代码版本:是的,它们的作用域是for语句的块:我添加了对spec的引用。@PaulHankin否,您的示例有点不同。mylist不应是点的列表。一个简单的修复方法是,我的示例演示了如何不使用那么多额外的类型和指针来实现与代码相同的结果。如果您有充分的理由使用这么多类型、容器结构、指针、指向切片的指针等等,那么您可以忽略这些,但我觉得这些建议对您很有用。