Tuesday 23 May 2017

Golang gotcha #2: Methods without pointer receivers cannot modify the actual receiver object

One gotcha that most newcomers to the language run into involves pointer receivers. A method on a struct is declared with a receiver such as those above. Meow is a method on struct type Cat with a receiver of c. c here is similar to self in Python, though we name it by convention as an acronym of the struct name rather than self. In the first example, when Meow() is called, c, the instance the method is called on, is passed as though it were an argument of the method.

If the method were instead defined with a pointer receiver as in the second example, then c would evaluate to a reference to the Cat instance.

Thus the impact of using non-pointer receivers is:
1) Additional memory overhead from copying.
2) Immutability of the actual receiver. This is the major gotcha, this means that methods declared with a non-pointer receiver cannot persist changes to the instance outside the scope of the method.

See this example for a demonstration:

I think that non-pointer receiver methods are more trouble than they are worth. It may seem wise to use them on methods that do not mutate the instance such as Get() and the like but there is a real danger it will come back to bite you in the form of a hard to diagnose bug.

Notable mention in golang docs:

Golang gotcha #1: Taking references to loop variables

The indomitable Smiler at Alton Towers
Holder of the world record for the most inversions, at 14

A colleague of mine ran into a bit of a golang gotcha recently. This relates to taking references / pointers to loop variables. I was already aware of the danger of this in the usage of closures. Closures access all variables in scope, implicitly by reference, this is in contrast to a function argument which is passed by value, this is demonstrated in the following example:


The closure in the for loop evaluates a reference to i rather than a copy of it, thus as i changes, the value that each goro has a reference to changes. In contrast, passing i as an argument to a function takes a copy. In general I dislike closures, they have a dirty scope, it is unlikely they use all variables in their scope, thus they have low cohesion and are harder to reason about. In Golang it is idiomatic to favour the explicit over the implicit, if you need access to a variable, you are better off explicitly passing it as an argument.

Now, onto the example which inspired this post:


In this case, similarly to the previous example we take a reference to a loop variable. However, the loop variable is reused in the further iterations of the loop. Thus our reference is now to a different value. It pays to be careful with loop variables and not to take references to them! An alternative solution as demonstrated in the example is to take the index or to take a copy.

Hopefully this post has illustrated some of the dangers of taking references to loop variables.

PS. I went on the Smiler last year, it really is quite good.