User Tools

Site Tools


variable_models_in_go

This is an old revision of the document!


Variable models in Go

One characteristic of Go that was not clear to me 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 confusion 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 special types like map, chan, and slices of any type behave as references; string variables are also references in Go, but this is never a problem because strings are immutable. Problems happen, for example, when you pass a slice to a function and the function changes its content when you were not expecting it.

But isn't Go all about values?

Yes. But what are the values?

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 not copied. The value of a slice is a hidden struct describing it. That struct 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.

So the only data copied when you pass a slice to a function is that 3-field struct. 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)

References

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.1514732153.txt.gz · Last modified: 2017/12/31 06:55 by luciano