User Tools

Site Tools


This is an old revision of the document!

Variable models in Go

One of the trickiest things to understand in Go is that it has three variable models: values, references, and pointers. In contrast, Java has two (values and references) and popular dynamic languages – Python, Ruby, Javascript – have only one: references.

The problem is compounded by the fact that there is no syntactic cue to distinguish values and references in Go. Pointer variable declarations are explicit thanks to the * symbol. In Java, reference types are always written with an initial upper case, like String.

In Go there are no visual clues: you have to know and remember that variables of built-in types like string, map, chan and slices behave as references.

But isn't Go all about values?

In a way, yes. If you have an array with 1 million elements and you pass that array to a function directly (without using a pointer), the entire array with 1 million elements will be copied to the parameter variable, which is a local variable in the function.

However, if you pass a slice to a function, the elements are copied. The value that is copied is a hidden struct describing the slice. It has three fields:

  • a pointer to the first element;
  • an int holding the current length of the slice;
  • an int holding the capacity – the maximum number of elements the slice can contain.

This has two very important consequences:

  1. Passing a slice to a function is efficient, regardless of the length of the slice.
  2. The function can change the slice at will, and the effects will be seen by the caller.

Other types like map, chan and string are also represented by implicit structs containing pointers. The string type is immutable, so the fact that its handled like a reference variable is a harmless optimization. But maps and channels are mutable, so the caveats I mentioned for slices also apply: always keep in mind that passing one of those to a function means the function will be able to modify the referenced value, whether you want it or not.

In the case of a channel, we naturally expect it will be consumed or fed by the function. For maps and slices, that is not the case, and subtle bugs may happen if a function is unexpectedly changing the data we pass to it.

It is interesting to note that slices, maps and channels are built using the make function, which is one of the few generic functions in Go: it takes types as arguments.1)


This is one example of magic in the language: mortal Go users are not able to create generic functions, but the Go creators can.
variable_models_in_go.1514731557.txt.gz · Last modified: 2017/12/31 06:45 by luciano