Whether a loop is involved is irrelevant, it comes up any time a value referenced a closure might outlast the environment which created the closure. That can happen in loops but also if conditions or normal function calls. The value might be promoted to the heap, but in trivial cases like the above it could be copied directly on the new goroutine's stack. Or if escape analysis says the closure can't outlast the environment, it will use the same stack.
for _, v := range values {
go func(v string) {
fmt.Println(v)
done <- true
}(v)
}
No, the 16 bytes will be copied onto the goroutine's stack.Do you think it's smart enough to do the trivial transformation of the first into the second?
Right, I understand the difference between a variable and a region of memory.
> If `values` is an array of strings, do you think the following makes a new string on the heap (that is, a new 16 byte heap allocation, internally pointing to the same variable-sized region of memory)?
No.
> Do you think it's smart enough to do the trivial transformation of the first into the second?
I guess I'm surprised that the semantics for the `v := v` in a loop thing are "this will get allocated somewhere else such that it's guaranteed not to be stomped on by another loop iteration". For example:
for _, v := range []int{2, 4, 6} {
v := v
mut := sync.Mutex{}
go func() {
mut.Lock()
fmt.Println(v)
mut.Unlock()
done <- true
}()
go func() {
mut.Lock()
v = 1
mut.Unlock()
done <- true
}()
}
If it allocates `v` on the goroutines' stacks, then it won't catch the mutation, so presumably it allocates on the heap at least in that case. In whatever case, the semantics are surprising to me.
Probably, hence my "How does that work?" question.
I don't doubt your example--I know it works, but I'm struggling to understand how it manages to work, since (as I understand it) the closure presumably captures the environment, which presumably means taking the address to the new `v` which presumably is the same address for each loop iteration?