Comment by gethly

4 days ago

Every language has an arsenal of footguns. Go is no different. I would say that overall it is not too bad, comparatively.

From all the listed cases, only the first one is easy to get caught by, even as an experienced developer. There, the IDE and syntax highlighting is of tremendous help and for general prevention. The rest is just understanding the language and having some practice.

I'm still relatively new to Go, but I've never seen closures used that often thus far in production code. Is it really a common practice?

  • That first example is an unintended closure, since the err at the top level actually has nothing to do with the errs in the goroutines. I have seen that sometimes, although the use of = rather than := normally makes it obvious that something dodgy is going on.

    As to whether it's a common pattern, I see closures on WaitGroups or ErrGroups quite often:

      workerCount := 5
      var wg sync.WaitGroup
      wg.Add(workerCount)
    
      for range workerCount {
        go func() {
          // Do work
          wg.Done()
        }()
      }
    
      wg.Wait()
    

    You can avoid the closure by making the worker func take a *sync.WaitGroup and passing in &wg, but it doesn't really have any benefit over just using the closure for convenience.

The first one should be caught by the Go race detector AFAIK. It will warn about the conflicting write accesses to err when both goroutines run.