将内容追加到嵌套结构的切片中无法正常工作。

huangapple go评论74阅读模式
英文:

Append content to slice into a nested struct does not work

问题

我有两个嵌套的结构体,如下所示:

type Block struct {
    ID       string
    Contents []string
}

type Package struct {
    Name   string
    Blocks []Block
}

当我尝试在特定的块中追加新的内容时,原始包(p)不会改变。

for _, b := range p.Blocks {
    if b.ID == "B1" {
        fmt.Println("Adding a new content")
        b.Contents = append(b.Contents, "c3")
    }
}

示例:

https://play.golang.org/p/5hm6RjPFk8o

英文:

I have two nested structs like this:

type Block struct {
	ID       string
	Contents []string
}

type Package struct {
	Name   string
	Blocks []Block
}

Original package (p) does not change when I try to append a new Content in a specific block.

for _, b := range p.Blocks {
	if b.ID == "B1" {
		fmt.Println("Adding a new content")
		b.Contents = append(b.Contents, "c3")
	}
}

Example:

https://play.golang.org/p/5hm6RjPFk8o

答案1

得分: 7

这是发生的原因是因为这行代码:

for _, b := range p.Blocks {

在切片中创建了每个元素的副本,在这种情况下,这意味着创建了切片中每个Block的副本。因此,当你在循环体中进行更改时,你是在对副本进行更改,而不是对切片中的Block进行更改。

如果你改为使用索引来获取每个Block的指针,例如:

for i := range p.Blocks {
    b := &p.Blocks[i]
    // 修改 b ...
}

它将按预期工作:

https://play.golang.org/p/h_nXEX9oWRT

或者,你可以对副本进行更改(就像你的原始代码中那样),然后将修改后的值复制回切片:

for i, b := range p.Blocks {
    // 修改 b ...
    p.Blocks[i] = b
}

https://go.dev/play/p/kVHTk-OTyC3

甚至更进一步,你可以在切片中存储指向Block的指针(而不是Block本身),在这种情况下,你的循环将复制指针,这是访问原始指针指向的Block的有效方式:

https://go.dev/play/p/I9-EyV_iCNS

英文:

This is happening because this line:

for _, b := range p.Blocks {

creates a copy of each element in the slice, and in this case this means creating a copy of each Block in the slice. So when you then make the changes in the loop body, you are making them to the copy of the Block, instead of to the Block in the slice.

If you instead use the index to get a pointer to each Block, e.g.

for i := range p.Blocks {
    b := &p.Blocks[i]
    // modify b ...
}

it works as expected:

https://play.golang.org/p/h_nXEX9oWRT

Alternatively, you can make the changes to the copy (as in your original code), and then copy the modified value back to the slice:

for i, b := range p.Blocks {
    // modify b ...
    p.Blocks[i] = b
}

https://go.dev/play/p/kVHTk-OTyC3

Even further, you could instead store pointers to Block in the slice (instead of the Block themselves), in which case your loop would be making a copy of the pointer, which is a valid way to access the Block the original pointers points to:

https://go.dev/play/p/I9-EyV_iCNS

答案2

得分: 1

当你在循环遍历一个切片时,从切片中检索到的每个单独的值都是对应元素的副本。因此,要修改切片中的元素,而不是副本,你可以使用索引表达式直接访问该元素。或者你可以使用指针。请注意,指针也会被复制,但复制的指针将指向与切片中的元素相同的地址,因此可以用于直接修改相同的数据。

你可以使用索引:

for i := range p.Blocks {
    if p.Blocks[i].ID == "B1" {
        fmt.Println("Adding a new content")
        p.Blocks[i].Contents = append(p.Blocks[i].Contents, "c3")
    }
}

https://play.golang.org/p/di175k18YQ9

或者你可以使用指针:

type Block struct {
    ID       string
    Contents []string
}

type Package struct {
    Name   string
    Blocks []*Block
}

for _, b := range p.Blocks {
    if b.ID == "B1" {
        fmt.Println("Adding a new content")
        b.Contents = append(b.Contents, "c3")
    }
}

https://play.golang.org/p/1RjWlCZkhYv

英文:

When you are looping over a slice, each of the individual values retrieved from the slice is a copy of the corresponding element in the slice. So to modify the element in the slice, instead of the copy, you can access the element directly using the indexing expression. Or you can use pointers. Note that pointers are also copied but the copied pointer will point to the same address as the element in the slice and therefore can be used to directly modify the same data.


You can use indexing:

for i := range p.Blocks {
    if p.Blocks[i].ID == "B1" {
        fmt.Println("Adding a new content")
        p.Blocks[i].Contents = append(p.Blocks[i].Contents, "c3")
    }
}

https://play.golang.org/p/di175k18YQ9


Or you can use pointers:

type Block struct {
    ID       string
    Contents []string
}

type Package struct {
    Name   string
    Blocks []*Block
}

for _, b := range p.Blocks {
    if b.ID == "B1" {
        fmt.Println("Adding a new content")
        b.Contents = append(b.Contents, "c3")
    }
}

https://play.golang.org/p/1RjWlCZkhYv

huangapple
  • 本文由 发表于 2021年7月31日 23:49:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/68603643.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定