0

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?

Omar Hayam
  • 21
  • 4
  • 1) [Why is a map value in one function affected by an entry to the map in another function?](https://stackoverflow.com/questions/39605295/why-is-a-map-value-in-one-function-affected-by-an-entry-to-the-map-in-another-fu/39605565#39605565); and 2) [Why can't Go iterate maps in insertion order?](https://stackoverflow.com/questions/28930416/why-cant-go-iterate-maps-in-insertion-order/28931555#28931555) – icza Jun 11 '17 at 18:06
  • 1
    Go has no notion of "pass by reference" so your question makes no sense. Stop thinking about pointers as "pass by reference". It's not. – Volker Jun 11 '17 at 18:18

1 Answers1

5

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

Yerken
  • 1,944
  • 1
  • 14
  • 18
  • 1
    It's an implementation detail, but if you're writing about it, passing a map value is actually passing / copying a single **pointer** to a `runtime.hmap` value. Passing a copy of the header would be bad as not all fields are pointers (and modifying e.g. the `count` field – which is the length – would not be reflected at the caller). – icza Jun 11 '17 at 18:56