英文:
Golang Slice Indexing Value Or Reference?
问题
我正在学习Go语言,并对通过索引切片获取的"thing"感到困惑。
假设我们有一个类型为bag的结构体:
type bag struct {
    item string
}
然后,考虑我们有一个bag的列表:
itemBag := []bag{
    {item: "item1"},
    {item: "item2"},
    {item: "item3"},
}
现在,我尝试更改itemBag变量的第一个元素的内容。天真地,我做了以下操作:
item1 := itemBag[0]
item1.item = "not item1"
fmt.Printf("itemBag[0].item == 'not item1'? %v", itemBag[0].item == "not item1")
// 控制台打印 false
我得到的答案是false,因为(如果我理解正确,请纠正我)当我们执行item1 := itemBag[0]时,我们正在复制itemBag[0]的值并将其赋给item1。我相信这就是这篇博客所说的意思。
为了证明这一点,我尝试获取itemBag[0]的指针并修改其值,结果,我实现了我想要做的事情:
item1 := &itemBag[0]
item1.item = "not item1"
fmt.Printf("itemBag[0].item == 'not item1'? %v", itemBag[0].item == "not item1")
// 控制台打印 true
如果我们说在切片索引(即slice[index])时,我们将获得其值的副本而不是底层项,这是非常有道理的。
现在,当我尝试以下操作时:
itemBag[0].item = "not item1"
fmt.Printf("itemBag[0].item == 'not item1'? %v", itemBag[0].item == "not item1")
结果是true,这不是我预期的。因为它看起来好像itemBag[0].item在语法上与先将itemBag[0]赋给一个变量,然后引用其底层项是一样的。
我觉得我在Go语言课程中漏掉了一个基础章节,请问是否有任何指导/解释?非常感谢!
我尝试过搜索诸如"slice indexing pass by reference"之类的内容,但无法找到准确的关键词进行搜索。
英文:
I am learning go and is confused by the "thing" we get out of indexing a slice.
Consider that we have a struct of type bag:
type bag struct {
    item string
}
Then, consider that we have a list of bags:
itemBag := []bag{
    {item: "item1"},
    {item: "item2"},
    {item: "item3"}
}
Now, I try to change the content of the first element of our itemBag variable. Naively, I do the following:
item1 := itemBag[0]
item1.item = "not item1"
fmt.Printf("itemBag[0].item == 'not item1'? %v", itemBag[0].item == "not item1")
// console print false
I got the answer as false, because (I think, do correct me if I am wrong) when we do item1 := itemBag[0], we are making a copy of the value of itemBag[0] and assign it to item1. Which I believe is what is meant by this blog here.
To prove that, I try to obtain the pointer of the itemBag[0] instead and modify its value, and voila, I achieve what I wanted to do:
item1 := &itemBag[0]
item1.item = "not item1"
fmt.Printf("itemBag[0].item == 'not item1'? %v", itemBag[0].item == "not item1")
// console print true
Which makes a lot of sense if we say that when we do slice indexing (i.e. slice[index]) we'll get copy of its value instead of the underlying item.
Now, when I do the following instead:
itemBag[0].item = "not item1"
fmt.Printf("itemBag[0].item == 'not item1'? %v", itemBag[0].item == "not item1")
The result is true, which is not what I expected. Because it looks like itemBag[0].item is syntatically the same as if I first assign itemBag[0] to a variable and subsequently referring to its underlying item.
I feel like I am missing a basic chapter in the golang course, please, any redirection/explanation will be highly appreciated!
Tried googling things like "slice indexing pass by reference" but couldn't pinpoint the exact keywords to google.
答案1
得分: 5
赋值操作会复制数值。所以当你执行:
item1 = itemBag[0]
你创建了一个 bag 类型对象 itemBag[0] 的副本。现在 item1 拥有了这个副本,对它所做的任何修改都会在副本上进行。
itemptr = &itemBag[0]
赋值操作的右侧是一个指针,所以这个操作会创建指针的副本并将其赋值给 itemptr。当你执行 itemptr.item="x" 时,它等同于 (*itemptr).item=x,所以指针的内容被修改了。
itemBag[0] 是一个可寻址的值,也就是说你可以取得它的地址。
https://go.dev/ref/spec#Address_operators
表达式 itemBag[0].item 也是一个可寻址的值,所以你可以给它赋值,这个赋值会反映在切片本身上。
然而,下面的代码不是一个可寻址的值:
m := map[int]bag{1: bag{item: "str"}}
m[1].item = "str1"
这是因为 m[1] 返回的是映射中一个值的副本,对它进行赋值会丢失。然而,下面的代码是可寻址的:
m := map[int]*bag{1: &bag{item: "str"}}
m[1].item = "str1"
这会设置映射中的 bag 的 item 字段。
英文:
Assignment operation copies values. So when you do:
item1 = itemBag[0]
you create a copy of the object at itemBag[0], which is of type bag. Now item1 has a copy of it, and any modifications you make to it will be made on the copy.
itemptr = &itemBag[0]
The right-side of the assignment is a pointer, so this operation creates a copy of that pointer and assigns it to itemptr. When you do itemptr.item="x", it is equivalent to (*itemptr).item=x, so the contents of the pointer is modified.
itemBag[0] is an addressable value, i.e. you can take the address of it.
https://go.dev/ref/spec#Address_operators
The expression itemBag[0].item is also an addressable value, so you can assign something to it, and it will be reflected in the slice itself.
The following, however, is not an addressable value:
m:=map[int]bag{1:bag{item:"str"}}
m[1].item="str1"
This is because m[1] returns a copy of a value in a map, and assigning a value to it would be lost. The following, however, is addressable:
m:=map[int]*bag{1:&bag{item:"str"}}
m[1].item="str1"
This would set the item field of the bag in the map.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论