There is no pass-by-reference in Go

My post on pointers provoked a lot of debate about maps and pass by reference semantics. This post is a response to those debates.

To be clear, Go does not have reference variables, so Go does not have pass-by-reference function call semantics.

What is a reference variable?

In languages like C++ you can declare an alias, or an alternate name to an existing variable. This is called a reference variable.

#include <stdio.h>

int main() {
        int a = 10;
        int &b = a;
        int &c = b;

        printf("%p %p %p\n", &a, &b, &c); // 0x7ffe114f0b14 0x7ffe114f0b14 0x7ffe114f0b14
        return 0;
}

You can see that a, b, and c all refer to the same memory location. A write to a will alter the contents of b and c. This is useful when you want to declare reference variables in different scopes–namely function calls.

Go does not have reference variables

Unlike C++, each variable defined in a Go program occupies a unique memory location.

package main

import "fmt"

func main() {
        var a, b, c int
        fmt.Println(&a, &b, &c) // 0x1040a124 0x1040a128 0x1040a12c
}

It is not possible to create a Go program where two variables share the same storage location in memory. It is possible to create two variables whose contents point to the same storage location, but that is not the same thing as two variables who share the same storage location.

package main

import "fmt"

func main() {
        var a int
        var b, c = &a, &a
        fmt.Println(b, c)   // 0x1040a124 0x1040a124
        fmt.Println(&b, &c) // 0x1040c108 0x1040c110
}

In this example, b and c hold the same value–the address of a–however, b and c themselves are stored in unique locations. Updating the contents of b would have no effect on c.

But maps and channels are references, right?

Wrong. Maps and channels are not references. If they were this program would print false.

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)
}

If the map m was a C++ style reference variable, the m declared in main and the m declared in fn would occupy the same storage location in memory. But, because the assignment to m inside fn has no effect on the value of m in main, we can see that maps are not reference variables.

Conclusion

Go does not have pass-by-reference semantics because Go does not have reference variables.

Next: If a map isn’t a reference variable, what is it?