【问题标题】:Strange mutation of map when passed value vs when passed by reference (Golang)传递值与通过引用传递时映射的奇怪突变(Golang)
【发布时间】:2017-06-11 17:34:08
【问题描述】:

第一种情况中,我将映射按值传递给: 主包

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[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]

我的期望如下:

  • 当按值传递时映射静音
  • 当通过参考地图传递时像 second 情况一样静音。 然而,在 first 情况下,地图被静音,但顺序相反(与 second 情况相比)。

为什么会这样?

【问题讨论】:

标签: function go parameter-passing mutability


【解决方案1】:

Go 中没有引用传递这样的东西。每当你传递任何东西(指针、切片头、映射)时,它总是按值传递。问题是通过值传递的究竟是什么(即类型的实际 value 是什么)。

当你传递一个映射时,你传递一个指针的副本到它的头部,它包含一组指向桶的指针,就像在哈希表的实现中一样。 https://github.com/golang/go/blob/master/src/runtime/hashmap.go#L106

因此,将指针传递给映射很少有意义,因为复制映射头指针的操作非常便宜。

现在为什么顺序不同,这仅仅是由于映射的内部实现,键的范围以随机方式发生。同样,这只是一个实现细节。

编辑:

正如@icza 正确指出的那样,传递地图实际上是将指针 的副本传递给地图头,而不是地图头本身。很抱歉造成混乱

【讨论】:

  • 这是一个实现细节,但如果你正在写它,传递一个映射值实际上是将一个指针传递/复制到一个runtime.hmap值。传递标头的副本会很糟糕,因为并非所有字段都是指针(并且修改例如 count 字段 - 这是长度 - 不会反映在调用者处)。
猜你喜欢
  • 1970-01-01
  • 2020-05-26
  • 2013-04-21
  • 2012-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-30
相关资源
最近更新 更多