Странная мутация карты при передаче значения по сравнению с передачей по ссылке (Golang)

В первом случае я передаю карту по значению: 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[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]

Мои ожидания были следующие:

  • при передаче по значению карта не приглушается
  • При переходе по ссылке карта приглушается, как и во втором случае. Однако в первом случае карта была приглушена, но в обратном порядке (по сравнению со вторым случаем).

Почему так происходит?


person Omar Hayam    schedule 11.06.2017    source источник
comment
В Go нет понятия передачи по ссылке, поэтому ваш вопрос не имеет смысла. Перестаньте думать об указателях как о передаче по ссылке. Это не.   -  person Volker    schedule 11.06.2017


Ответы (1)


В Go нет такой вещи, как передача по ссылке. Всякий раз, когда вы что-либо передаете (указатель, заголовок слайса, карта), оно всегда передается по значению. Вопрос в том, что именно передается по значению (т.е. что такое фактический value типа).

Когда вы передаете карту, вы передаете копию указателя в ее заголовок, который содержит набор указателей на сегменты, как в реализации HashTable. https://github.com/golang/go/blob/master/src/runtime/hashmap.go#L106

Поэтому редко имеет смысл передавать указатель на карту, потому что операция копирования указателя заголовка карты чрезвычайно дешева.

Теперь, почему порядок отличается, это просто из-за внутренней реализации карты, ранжирование по ключам происходит случайным образом. Опять же, это просто детали реализации.

ИЗМЕНИТЬ:

Как правильно заметил @icza, передача карты фактически является передачей копии указателя на заголовок карты, а не самого заголовка карты. Извините за путаницу

person Yerken    schedule 11.06.2017
comment
Это деталь реализации, но если вы пишете об этом, передача значения карты на самом деле является передачей/копированием одного указателя на значение runtime.hmap. Передача копии заголовка была бы плохой, поскольку не все поля являются указателями (и изменение, например, поля count, которое является длиной, не будет отражено в вызывающем объекте). - person icza; 11.06.2017