【问题标题】:Are maps passed by value or by reference in Go?在 Go 中,map 是按值传递还是按引用传递?
【发布时间】:2017-04-02 12:50:11
【问题描述】:

在 Go 中映射是按值传递还是按引用传递?

总是可以像下面这样定义一个函数,但这是否有点矫枉过正?

func foo(dat *map[string]interface{}) {...}

返回值的问题相同。我应该返回指向地图的指针,还是将地图作为值返回?

目的当然是避免不必要的数据复制。

【问题讨论】:

  • blog.golang.org/go-maps-in-action:映射类型是引用类型,如指针或切片,所以上面的m的值为nil;它不指向初始化的地图。读取时,nil 映射的行为类似于空映射,但尝试写入 nil 映射会导致运行时恐慌;不要那样做。要初始化地图,请使用内置的 make 函数
  • Everything 在 Go 中都是按值传递的。有些值恰好是指针,或包含指针的结构。 (在某些情况下,如果您需要在某个地址重新分配地图值,您可能需要 *map
  • mh-cbon,Go 中没有引用类型。
  • @mh-cbon 我不是在谈论引用类型。我问的是地图是否通过引用传递,这相当于询问地图的地址是作为参数传递还是作为地图的“副本”传递(按值传递)。
  • @mh-cbon 没错,地图是指向 hmap 的指针。

标签: go


【解决方案1】:

在这个帖子里你会找到你的答案:

Golang: Accessing a map using its reference

您不需要在地图上使用指针。

映射类型是引用类型,如指针或切片[1]

如果您需要更改会话,您可以使用指针:

map[string]*Session

https://blog.golang.org/go-maps-in-action

【讨论】:

  • 为避免任何陷阱,请注意地图仅在初始化后通过引用传递,并且在函数内部重新初始化时,原始引用 不会更新。这是一个说明性的游乐场示例:play.golang.org/p/Q6vrAmmJWR6 或 Dave Cheny 的完整文章dave.cheney.net/2017/04/29/there-is-no-pass-by-reference-in-go
  • 错误 - 无论是否为 nil,地图总是以相同的方式通过。此外,如果不将指针传递给它并使用取消引用赋值,则无法重新初始化映射。相同的链接代码证明了后者,而前者很容易通过不初始化地图来证明。正如 19 次投票所显示的那样,围棋很容易出错。
  • 我个人认为(顺便提及)“切片是引用类型”是一种误导,因为它们不是真的吗?如果您将切片按值传递给函数,然后多次附加到该切片,则不能保证更新该切片的调用者副本 - 相反,被调用函数需要传回切片的新值。这是因为如果切片重新分配,您将获得切片的新 (因此 a = append(a, v) 而不仅仅是 append(a, v))映射没有这种语义,一旦创建映射可以在不产生新地图值的情况下更新。
【解决方案2】:

以下是 Dave Cheney 的 If a map isn’t a reference variable, what is it? 的部分内容:

映射值是指向runtime.hmap 结构的指针。

和结论:

结论

Map 与通道类似,但与切片不同,只是指向运行时的指针 类型。正如您在上面看到的,地图只是指向runtime.hmap 的指针 结构。

Map 与 a 中的任何其他指针值具有相同的指针语义 去程序。除了重写映射语法之外,没有什么神奇的 编译器调用runtime/hmap.go中的函数。

还有一点关于 map 语法的历史/解释的有趣之处:

如果地图是指针,它们不应该是*map[key]value吗?

这是一个很好的问题,如果映射是指针值,为什么 表达式make(map[int]int) 返回一个类型为 map[int]int。它不应该返回*map[int]int 吗?伊恩·泰勒 answered this recently in a golang-nuts线程1.

在早期,我们现在所说的地图被写成指针, 所以你写了*map[int]int。当我们意识到 没有人不写*map就写map

可以说将类型从 *map[int]int 重命名为 map[int]int,而 令人困惑,因为类型看起来不像指针,更少 比无法取消引用的指针形状的值更令人困惑。

【讨论】:

    【解决方案3】:

    没有。地图是默认参考。

        package main
    
        import "fmt"
    
        func mapToAnotherFunction(m map[string]int) {
            m["hello"] = 3
            m["world"] = 4
            m["new_word"] = 5
        }
    
        // func mapToAnotherFunctionAsRef(m *map[string]int) {
        // m["hello"] = 30
        // m["world"] = 40
        // m["2ndFunction"] = 5
        // }
    
        func main() {
            m := make(map[string]int)
            m["hello"] = 1
            m["world"] = 2
    
            // Initial State
            for key, val := range m {
                fmt.Println(key, "=>", val)
            }
    
            fmt.Println("-----------------------")
    
            mapToAnotherFunction(m)
            // After Passing to the function as a pointer
            for key, val := range m {
                fmt.Println(key, "=>", val)
            }
    
            // Try Un Commenting This Line
            fmt.Println("-----------------------")
    
            // mapToAnotherFunctionAsRef(&m)
            // // After Passing to the function as a pointer
            // for key, val := range m {
            //  fmt.Println(key, "=>", val)
            // }
    
            // Outputs
            // hello => 1
            // world => 2
            // -----------------------
            // hello => 3
            // world => 4
            // new_word => 5
            // -----------------------
    
        }
    

    来自 Golang 博客-

    Map 类型是引用类型,如指针或切片,所以上面 m 的值为 nil;它不指向初始化的地图。读取时,nil 映射的行为类似于空映射,但尝试写入 nil 映射会导致运行时恐慌;不要那样做。要初始化地图,请使用内置的 make 函数:

    // Ex of make function
    m = make(map[string]int)
    

    Code Snippet Link 玩它。

    【讨论】:

    • 它们不是引用类型(尽管它们是间接类型)。如果您分配给地图值,您将破坏它与其他变量的关系。 Go 没有引用类型,一切都是值。您可以使用func mapToAnotherFunction(m map[string]int) { m = nil } 来演示这一点,它不会清空地图,只是以本地方式覆盖其值。
    猜你喜欢
    • 2016-11-28
    • 2017-11-14
    相关资源
    最近更新 更多