Hacker News new | past | comments | ask | show | jobs | submit login

I think https://eli.thegreenplace.net/2019/go-internals-capturing-lo... describes the problem in more detail?



This post was great—thanks for posting it!

Interestingly, the reason the old i := i trick works is not at all what I thought!

The trick, for reference:

    for i := 0; i < 5; i++ {
     i := i  // the trick 
     go func() {
      print(i)
     }()
    }
What I assumed happened:

- The escape analyzer sees that the new `i` is passed to a goroutine, so it is marked as escaping its lexical scope

- Because it's escaping its lexical scope, the allocator allocates it on the heap

- One heap allocation is done per loop iteration, and even though the new `i` is captured by reference, each goro holds a unique reference to a unique memory location

What actually happens:

- Go's compiler has heuristics to decide whether to capture by reference or by value. One component of the heuristic is that values that aren't updated after initialization are captured by value

- The new i is scoped to the for loop body, and is not updated by the for loop itself. Therefore it's identified as a value that isn't updated after initialization

- As a result, the Go compiler generates code that captures `i` by value instead of by reference. No heap allocations or anything like that are done.

I recognize that the latter behavior is better, but if anyone with intimate knowledge of Go knows why the former doesn't (also) happen (is that even how Go works?) I would love to find out!


I don’t think it’s so complex. Without i := i, there’s only one i. With i := i, there’s one i per iteration.

Closure captures are always by reference.

Heap vs stack allocation don’t affect the language semantics.


Yup. The linked article is a little confused. It thinks that an optimization to pass by value is affecting the behavior. In reality it only passes by value when it is indistinguishable from passing by reference (and it thinks it would be cheaper).


There is no “trick”. It’s the language spec! That go func can take arguments. Just add the argument for clarity. The “trick” here is saving the declaration in the go func’s signature. ‘go func(i0 int) { .... }(i)’


That's a much better explanation for someone like me who isn't very familiar with Go.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: