英文:
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:
答案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:
答案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")
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论