Go's version 1 will be a year old in about 2 months, and while I've been writing various libraries and applications in it since its release, I'm still coming across subtle false equivalences that I've made coming over from Python.

Though there can be some confusion in the terminology, everything in Python is a reference. These references are always passed around by value, but in essence, all references to a thing have the same access to that thing, and all assignments change the reference of a particular name:

>>> def foo(l):
...     print id(l)
...     l = "something else"
...     print id(l)
... 
>>> x = (1, 2, 3)
>>> foo(x)
140725676402880
140725676539440
>>> x
(1, 2, 3)
>>> id(x)
140725676402880

In the code above, x doesn't change to "something else" because the code in foo is merely setting the variable l to refer to some new thing. It doesn't affect the binding of the variable x. However, as foo gets its parameter, it's clear that the reference of l and x are the same 140725676402880, but "changing" x requires mutating the data at that location, and cannot be done by reassigning l to some new reference instead.

Golang has pointers and values, which means you can write functions which change outer variables by explicitly passing the address of those variables to a function that takes a pointer to its type. Still, the assignment semantics are the same for each: a := b always creates a new copy of b called a. When b is a pointer, a gets a copy of the same location b points to, and can be re-pointed elsewhere independently of b; when b is a value, a is a new copy of that value occupying different memory. However, Go goes to some lengths to make pointers and values behave similarly in certain situations:

type T struct{ Name string }

func (t T) String() string {
    return t.Name
}

func main() {
    vals := []T{T{"John"}, T{"Jane"}}
    ptrs := []*T{&T{"John"}, &T{"Jane"}}
    fmt.Println(vals)
    fmt.Println(ptrs)
    fmt.Println(vals[0].String(), ptrs[0].String())
}

This will print [John Jane] twice, and John John, because go knows it can call T.String() for a *T. If T.String() were defined on as func (t *T) String... instead, go would still call that method on addressable T's, and have a build error trying to call it on non-addressable literal T's (it will of course take literal *T's, but you'll have to use parens, eg. (&T{"John"}).String())

When you set a variable t := vals[0], it becomes a copy of that first val; when you set t := ptrs[0], it becomes a copy of the pointer. This is still obvious, but has subtle repercussions where you might not think of things as assignment:

// let's create a list of pointers to each T in vals
ptrcopy := []*T{}
for _, t := range vals {
    ptrcopy = append(ptrcopy, &t)
}
fmt.Println(ptrcopy)

The code above does not work, and will print out [Jane Jane]. The loop variable t is a single variable throughout the loop, and as you loop over vals, it will get a copy of the next T, but the address (&t) stays the same. The equivalent code on the ptrs slice will work, because t will be a copy of the next address to a T, and the addresses will change as you loop over them and append them to ptrcopy. In order to make this work on vals, we'd have to do this:

for i, _ := range vals {
    ptrcopy = append(ptrcopy, &vals[i])
}

When you stop to think what golang's for loop is actually doing, this behavior isn't all that surprising, but since I was working under the false notion that for _, x := range xs was basically the same as Python's for x in xs, it bit me in a very confusing way.

Feb 2 2013