2020-02-28
One reason for Go to prefer providing indexes in for ... range
loops
I was recently reading Nick Cameron's Early Impressions of Go from a Rust Programmer (via). One of the things Cameron did not like about Go was, well, let me quote directly from the article:
for ... range
returns a pair of index/value. Getting just the index is easy (just ignore the value) but getting just the value requires being explicit. This seems back-to-front to me since I need the value and not the index in most cases.
Implicitly, this is about ranging over slices and arrays, rather than strings (for which indexing is a bit different) or maps.
I can't know the reasons behind this decision of Go's, but I think
that one good reason for this approach is to encourage people towards
using the value in place by accessing it through an index into the
array or slice. The potential problem with using the value directly
from the range statement is that when you use the value from a
for ... range
over a slice or array, you're actually using a
copy of it. When you write:
for i, v := range aslice { fmt.Printf("%p %p\n", &v, &aslice[i]) }
The two pointer values are not the same (and v
is the same pointer
value all the time). The for
loop variable v
is a copy of
aslice[i]
, not the same as it. Sometimes this copy will be basically
free, for instance if you're ranging over a slice of something
small. But if you're ranging over a slice of decent sized structures
or something else that's big, the copy is not free at all and you
may well want to use them in place in aslice
.
(In some situations making a copy is not even correct, but you're
not likely to encounter those with slices or arrays. For instance,
you're probably not going to have an array of sync.Mutex
es, or structures containing
them. More likely you'd have an array of pointers to them, and copying
pointers to mutexes (and structures containing them) is perfectly
valid.)
By returning the index first (and making it easy to get the index without the value), Go quietly nudges you toward using the index to directly access the value, rather than forcing it to make a copy of the value for you. Go only has to materialize a copy of the value if you go out of your way to ask for it, and it hopes that you won't. At the very least, this nudge may get you to think about it.
(I also feel that it fits in with Go's general philosophy and
approach. The natural starting point of a for ... range
over an array
or a slice is an index, so that's what you get to start with; making a
copy of the value is an extra step that the compiler has to go out of
its way to do, so it's not natural to have it the first thing produced.)