Golang切片的索引是值还是引用?

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

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"

这会设置映射中的 bagitem 字段。

英文:

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.

huangapple
  • 本文由 发表于 2023年3月18日 00:56:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75770239.html
匿名

发表评论

匿名网友

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

确定