I don't know much about Go but the design seems very intuitive to me. You're doing something like (let ((i variable)) (loop (setf i ...) ...body)), which if there is a closure in the loop body will capture the variable i and also subsequent mutations.
The fix is to make a new variable for each iteration, which is less obvious implementation wise but as per the post works better if you're enclosing over the loop variable.
While I agree that the design is very clear for the cases illustrated here, and I am a bit puzzled on why the Go designers chose to change these as well, the design is not at all clear for the other case:
for i, v := range []int{1, 2, 3} {
funcs = append(funcs, func() {fmt.Printf("%v:%v, ", i, v)})
}
for _, fun := range funcs {
fun()
} //prints 2:3, 2:3, 2:3
The reason why this happens is clear. But, it's not what people expect from the syntax, not at all. And it's also a behavior that is never useful. There is 0 reason to capture the loop variables, as evidenced by the fact that none of the languages that have started like this and taken a breaking change to switch to the expected behavior has found even a single bug caused by this breaking change.
False. There are cases where it is useful to have the loop variable available directly. For example, you can add one to the loop variable to skip an iteration, which would not work with an iteration-local loop variable.
In a for-in-range loop, the variables are read-only inside the loop body, so there is no way to skip this.
I do agree that there are reasons to modify the iteration variable in a C-style for loop, so I am surprised that those loops are being modified as well. C#, which went through a similar change, did NOT apply such a change for those for loops.
The fix is to make a new variable for each iteration, which is less obvious implementation wise but as per the post works better if you're enclosing over the loop variable.