Golang Pass Map Type Parameter

Posted by Henry Du on Sunday, September 13, 2020

Golang Pass Map Type Parameter

Observation

When I read this blog section “But maps and channels are references, right?”, I realize that the following code should print true, because Golang pass the map by value, not map address.

package main

import "fmt"

func fn(m map[int]int) {
        m = make(map[int]int)
}

func main() {
        var m map[int]int
        fn(m)
        fmt.Println(m == nil)
}

Analysis

This is running result. When the program starts run main function, the active stack frame in memory belongs to main. The m’s memory is 0xc00000e028. When program calls func fn(m map[int]int), the program push stack and switch to fn frame as an active frame. When pushing the stack, the parameter m’s value is copied from main, which is nil. It copies value from memory 0xc00000e02 to memory 0xc00000e038. After func fn returns, the m is the one which is not initialized. Here is the example.

However, when we pass the address of m to the func fn, the result is false. Here is the example.

package main

import "fmt"

func fn(m *map[int]int) {
    fmt.Printf("%p\n", m)
    *m = make(map[int]int)
}

func main() {
    var m map[int]int
    fmt.Printf("%p\n", &m)
    fn(&m)
    fmt.Println(m == nil)
}

This is because when pushing the stack, the parameter is copy of the address of the m defined in func main. The map initialization is for the same object at the same memory location.

Caveats

Golang has three data type categories: build-in type like string or bool, reference type like slice/array or map, and composite type like struct. It is highly recommended to use value semantic for the build-in type and reference type. Therefore, for this post, *map is rarely used, even though it works.

For the value semantics convention, any mutation happened inside function should return a new data to the caller.

Conclusion

Many documents have mentioned that, Golang only pass the parameters by value, not reference. My understanding is that, the behavior is the same as C/C++. The function is running in a certain stack frame, all local variables, including the copied the parameters, will be gone after function returned. However, all variables referenced by pointer can be manipulated by the function and mutated accordingly.