6 ms·
I wonder what the thought process of the Go designers was when coming up with that approach. Function scope is rarely what a user needs, has major pitfalls, and
by CodesInChaos 8mo ago
I wonder what the thought process of the Go designers was when coming up with that approach. Function scope is rarely what a user needs, has major pitfalls, and is more complex to implement in the compiler (need to append to an unbounded list).
- mort96 8mo agoI hate that you can't call defer in a loop. I hate even more that you can call defer in a loop, and it will appear to work, as long as the loop has relatively few iterations, and is just silently massively wasteful.
- usrnm 8mo agoThe go way of dealing with it is wrapping the block with your defers in a lambda. Looks weird at first, but you can get used to it.
- mort96 8mo agoI know. Or in some cases, you can put the loop body in a dedicated function. There are workarounds. It's just bad that the wrong way a) is the most obvious way, and b) is silently wrong in such a way that it appears to work during testing, often becoming a problem only when confronted with real-world data, and often surfacing only as being a hard-to-debug performance or resource usage issue.
- deleted 8mo ago[deleted]
- 9rx 8mo agoWhat's the use-case for block-level defer? In a tight loop you'd want your cleanup to happen after the fact. And in, say, an IO loop, you're going to want concurrency anyway, which necessarily introduces new function scope.
- mort96 8mo ago> In a tight loop you'd want your cleanup to happen after the fact. Why? Doing 10 000 iterations where each iteration allocates and operates a resource, then later going through and freeing those 10 000 resources, is not better than doing 10 000 iterations where each iteration allocates a resource, operates on it, and frees it. You just waste more resources. > And in, say, an IO loop, you're going to want concurrency anyway This is not necessarily true; not everything is so performance sensitive that you want to add the significant complexity of doing it async. Often, a simple loop where each iteration opens a file, reads stuff from it, and closes it, is more than good enough. Say you have a folder with a bunch of data files you need to work on. Maybe the work you do per file is significant and easily parallelizable; you would probably want to iterate through the files one by one and process each file with all your cores. There are even situations where the output of working on one file becomes part of the input for work on the next file. Anyway, I will concede that all of this is sort of an edge case which doesn't come up that often. But why should the obvious way be the wrong way? Block-scoped defer is the most obvious solution since variable lifetimes are naturally block-scoped; what's the argument for why it ought to be different?
- deleted 8mo ago[deleted]
- nasretdinov 8mo agoLet's say you're opening files upon each loop iteration. If you're not careful you'll run out of open file descriptors before the loop finishes.
- mort96 8mo agoIt doesn't just have to be files, FWIW. I once worked in a Go project which used SDL through CGO for drawing. "Widgets" were basically functions which would allocate an SDL surface, draw to it using Cairo, and return it to Go code. That SDL surface would be wrapped in a Go wrapper with a Destroy method which would call SDL_DestroySurface. And to draw a surface to the screen, you need to create an SDL texture from it. If that's all you want to do, you can then destroy the SDL surface. So you could imagine code like this: strings := []string{"Lorem", "ipsum", "dolor", "sit", "amet"} stringTextures := []SDLTexture{} for _, s := range strings { surface := RenderTextToSurface(s) defer surface.Destroy() stringTextures = append(stringTextures, surface.CreateTexture()) } Oops, you're now using way more memory than you need!
- 0xjnml 8mo ago> I wonder what the thought process of the Go designers was when coming up with that approach. Sometimes we need block scoped cleanup, other times we need the function one. You can turn the function scoped defer into a block scoped defer in a function literal. AFAICT, you cannot turn a block scoped defer into the function one. So I think the choice was obvious - go with the more general(izable) variant. Picking the alternative, which can do only half of the job, would be IMO a mistake.
- aw1621107 8mo ago> AFAICT, you cannot turn a block scoped defer into the function one. You kinda-sorta can by creating an array/vector/slice/etc. of thunks (?) in the outer scope and then `defer`ing iterating through/invoking those.