Hacker News new | ask | show | jobs
by dualogy 3105 days ago
Crikey! You wouldn't wanna allocate closures in a loop anyway, would you? Even if the compiler is smart about it. Doesn't read so well either.. what's wrong with adding a simple argument to your anonymous-func allocated outside of a loop? It's readable, and there are no 'gotchas'. The args are evaluated at the point of defer/go, not func execution. Simples.

Shadowing bites you sooner or later if it becomes a habit and gains you almost nothing in real terms. I keep running into subtle nasties in coworkers' commits from quick'n'dirty-that-stayed "convenience" shadowings. Need I say `err`.. of course linters help, but aren't a given in a "bring-your-own chosen dev-env" team. =)

1 comments

> Crikey! You wouldn't wanna allocate closures in a loop anyway, would you?

Very often yes I do.

Go encourages synchronous APIs, making it up to the caller to add concurrency. This is great, in my opinion. E.g. this is a common pattern:

  var wg sync.WaitGroup
  ch := make(chan int, len(items))
  for _, item := range items {
    item := item
    wg.Add(1)
    go func() {
      defer wg.Done()
      ch <- doSomething(item)
    }()
  }
  wg.Wait()
  close(ch)
  for res := range ch {
    …
  }
Similar patterns with defer, although yes I'd say less often in a loop. Though in order to be "exception safe" (panic safe) I often do:

  foo, err := openFileOrSomething()
  defer foo.Close()
  [… do something …]
  err := foo.Close()
So that even if "do something" throws exception… err… panics… the file gets closed. And double-closing is safe. That's not a closure though, in this example. So maybe not so good.
None of your explanations/examples counter the fact that you could also declare your anonymous-func just above your loop in a local, ie. `doit:= func(item...` and then simply `go doit(item)` in the loop. You get: a leaner terser loop to later have to read through, no iteration-scope "gotcha", no need to elaborately spell out explicit shadowings for what are naturally semantically really func "args" already/anyway, at worst identical cost (or better)..

But well, guess it comes down to subjective stylistic preferences here =)

> So that even if "do something" throws exception… err… panics… the file gets closed.

Your defer as placed in your above example is already scheduled to run always, even on a later panic. (After all, how else could one `recover` from a `panic` if it wasn't for `defer`?) I don't see the point of the double-closing at all here..

Yeah, I could declare it above. But it's subjectively harder to read, having to jump around. Like you say: subjective.

> Your defer as placed in your above example is already scheduled to run always

Yes, brainfart. Sorry. This is from a completely separate recommendation to defer a close (dropping error return), but also manually close so that closing errors can be surfaced. Matters for e.g. writing files (not so much reading), especially when you don't flush manually.