Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

What I find an interesting design choice about Go’s approach (using ‘defer’) is that they are executed at the end of the function — not the end of the current block:

https://play.golang.org/p/q5n0P-mKrmS

This means that if you were to alter a function by placing parts of its body inside a loop, you may accidentally introduce O(n) buildup of deferred statements. This means that you could also be piling up resources associated with those resources (e.g., O(n) file handles).

Because the number of ‘defer’ calls is thus unbounded, it may be the case the compiler needs to generate code to store the list of pending closures on the heap. This may already happen for bounded functions if the compiler is unable to analyze it. In those cases it may thus be faster to resort to traditional C-like error handling.

Optimizer passes for eliminating this overhead were added to Go 1.13: https://golang.org/doc/go1.13#runtime



> What I find an interesting design choice about Go’s approach (using ‘defer’) is that they are executed at the end of the function

It's quite a bad and bizarre design choice actually. It's strictly inferior to the other alternative.

Zig took the correct approach with its 'defer' and 'errdefer' constructs, fixing the two flaws with golang's implementation.


You just explained why it's done this way. Because 'defer' is not scoped by block you can now use your standard constructs with blocks — if, for-loops, etc. — to initiate the defers. If your function opens a N files based on some parameter N, it can still use a loop and defer each's close dynamically.

Sure there's ways to work around it like putting another loop in a single defer but it's not completely braindead this way.

It's much more annoying to need to add syntax to "expand" the scope of a deferred beyond a codeblock when you need it than to just wrap some code in a function when you need it.


Is it the end of the function, or the end of the scope?

There's also no reason the compiler couldn't allow the deferred call once the last reference has been utilized (though in practice the common and performance minded case probably is to just use it as an additional goroutine that the context passes execution to when it queues any return messages).



> There's also no reason the compiler couldn't allow the deferred call once the last reference has been utilized

There's one reason not to do this - preserving the language semantics. Rust's version of RAII drops owned objects at the end of their scope for the same reason, even though it 'could' do better especially given NLL.




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

Search: