A humbling experience of misreading some simple (Go) code
Every so often, I get to have a humbling experience, sometimes in public and sometimes in private. Recently I was reading Go Range Loop Internals (via) and hit its link to this Damian Gryski (@dgryski) tweet:
Today's #golang gotcha: the two-value range over an array does a copy. Avoid by ranging over the pointer instead.
I ran the code on the playground, followed it along, and hit a 'what?' moment where I felt I had a mystery where I didn't understand why Go was doing something. Here is the code:
func IndexValueArrayPtr() { a := [...]int{1, 2, 3, 4, 5, 6, 7, 8} for i, v := range &a { a[3] = 100 if i == 3 { fmt.Println("IndexValueArrayPtr", i, v) } } }
Usefully, I have notes about my confusion, and I will put them here verbatim:
why is the IndexValueArrayPtr result '3 100'? v should be copied before a[3] is modified, and v is type 'int', not a pointer.
This is a case of me reading the code that I thought was there
instead of the code that was actually present, because I thought
the code was there to make a somewhat different point. What I had
overlooked in IndexValueArrayPtr
(and in fact in all three
functions) is that a[3]
is set on every pass through the loop,
not just when i == 3
.
Misreading the code this way makes no difference to the other two
examples (you can see this yourself with this variant), but it's crucial to how
IndexValueArrayPtr
behaves. If the a[3]
assignment was inside
the if
, my notes would be completely true; v
would have copied
the old value of a[3]
before the assignment and this would print
'3 4'. But since the assignment happens on every pass of the loop,
a[3]
has already been assigned to be 100 by the time the loop
gets to the fourth element and makes v
a copy of it.
(I think I misread the code this way partly because setting a[3]
only once is more efficient and minimal, and as noted the other two
functions still illustrate their particular issues when you do it
that way.)
Reading an imaginary, 'idealized' version of the code instead of the real one is not a new thing and it's not unique to me, of course. When you do it on real code in a situation where you're trying to find a bug, it can lead to a completely frustrating time where you literally can't see what's in front of your eyes and then when you can you wonder how you could possibly have missed it for so long.
(I suspect that this is a situation where rubber duck debugging helps. When you have to actually say things out loud, you hopefully get another chance to have your brain notice that what you want to say doesn't actually correspond to reality.)
PS: The reason I have notes on my confusion is that I was planning to turn explaining this 'mystery' into a blog entry. Then, well, I worked out the mystery, so now I've gotten to write a somewhat different sort of blog entry on it.
|
|