英文:
Strange mutation of map when passed value vs when passed by reference (Golang)
问题
在第一种情况下,我通过值传递了一个map:
package main
import (
"fmt"
"time"
)
func timeMap(z map[string]interface{}) {
z["updated_at"] = time.Now()
}
func main() {
foo := map[string]interface{}{
"Matt": 42,
}
timeMap(foo)
fmt.Println(foo)
}
输出结果是一个被修改的map:
map[updated_at:2009-11-10 23:00:00 +0000 UTC Matt:42]
在第二种情况下,代码几乎相同,只是通过引用传递:
package main
import (
"fmt"
"time"
)
func timeMap(z *map[string]interface{}) {
(*z)["updated_at"] = time.Now()
}
func main() {
foo := map[string]interface{}{
"Matt": 42,
}
timeMap(&foo)
fmt.Println(foo)
}
显然,结果不同:
map[Matt:42 updated_at:2009-11-10 23:00:00 +0000 UTC]
我的期望是:
- 通过值传递时,map不会被修改。
- 通过引用传递时,map会被修改,就像第二种情况一样。
然而,在第一种情况下,map被修改了,但顺序与第二种情况相反。
为什么会这样呢?
英文:
In the first case I pass a map to by value:
package main
import (
"fmt"
"time"
)
func timeMap(z map[string]interface{}) {
z["updated_at"] = time.Now()
}
func main() {
foo := map[string]interface{}{
"Matt": 42,
}
timeMap(foo)
fmt.Println(foo)
}
The output is a muted map:
map[updated_at:2009-11-10 23:00:00 +0000 UTC Matt:42]
In the second case the code is almost identical but for passing by reference:
package main
import (
"fmt"
"time"
)
func timeMap(z *map[string]interface{}) {
(*z)["updated_at"] = time.Now()
}
func main() {
foo := map[string]interface{}{
"Matt": 42,
}
timeMap(&foo)
fmt.Println(foo)
}
Obviously, the result differs:
map[Matt:42 updated_at:2009-11-10 23:00:00 +0000 UTC]
My expectations were the following:
- when passing by value map is not muted
- When passing by reference map is muted like in the second case.
However, in the first case map was muted but in the reverse order (compared to the second case).
Why does it happen so?
答案1
得分: 5
在Go语言中,不存在按引用传递的概念。无论你传递什么(指针、切片头部、映射),它都是按值传递的。问题在于究竟传递的是什么值(即类型的实际“值”是什么)。
当你传递一个映射时,你传递的是其头部的指针的副本,该头部包含指向桶的指针集合,就像哈希表的实现一样。因此,将指针传递给映射很少有意义,因为复制映射头部指针的操作非常廉价。
至于为什么顺序不同,这仅仅是由于映射的内部实现方式,遍历键的顺序是随机的。这只是实现细节。
编辑:
正如@icza正确指出的,传递映射实际上是传递指向映射头部的指针的副本,而不是映射头部本身。对于造成的混淆,我表示抱歉。
英文:
There is no such thing as passing by reference in Go. Whenever you pass anything (pointer, slice header, map) it is always passed by value. The question is what exactly is being passed by value (i.e. what is the actual value
of the type).
When u pass a map, you pass a copy of the pointer to its header, which contains a set pointers to the buckets, as in the implementation of the HashTable. https://github.com/golang/go/blob/master/src/runtime/hashmap.go#L106
Therefore it rarely makes sense to pass a pointer to the map, because the operation of copying a map header pointer is extremely cheap.
Now why the order is different, this is simply due to internal implementation of the map, ranging over the keys occurs in a random fashion. Again this is just an implementation details.
EDIT:
As @icza correctly pointed out, passing a map is actually passing a copy of a pointer to the map header, not the map header itself. Sorry for the confusion
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论