6 ms·
"The guarantees needed to avoid leaving the server in a bad state when handling panics would be impossible without the defer mechanism Go provides." I'm only p
by alec 13y ago
"The guarantees needed to avoid leaving the server in a bad state when handling panics would be impossible without the defer mechanism Go provides."
I'm only passingly familiar with defer, but I understand it to be equivalent to RAII in C++, Python's with statement, Common Lisp's unwind-protect, and others - does is actually provide something more, and if so, what?
- tptacek 13y agoIt's not at all equivalent to RAII, or really any of those other examples; it's a way of clearing up the control flow of a function that needs cleanup work before it returns, but it is a fussy and error-prone way of expressing scoped resource access. "Defer" is nice to have, and because it does less than scoped acquisition, it's easier to repurpose for other jobs; that's kind of thematic of Golang --- simple, orthogonal advances over C/Java/C++; a distinct lack of "theoretical" ambition.
- raybejjani 13y agoDeferred statements will run even when the program panics so you can ensure you clean up. It's more like a finally statement in some languages.
- pcwalton 13y agoGo's "defer" is not equivalent to RAII. It is function-scoped rather than block-scoped and has semantics based on mutating hidden per-function mutable state at runtime. For example: func Foo() { for i := 0; i < 5; i++ { if Something() { defer Whatever() } } // ... the compiler can't tell how many // Whatever()s run here ... } Compared to RAII as implemented in for example D with its "scope" statement, "defer" has much more complex semantics, inhibits refactoring since moving things to function bodies or inlining function bodies silently changes semantics, and cannot be optimized as easily, because of the dynamic aspects. IMHO, it has essentially no advantages over RAII and many disadvantages.
- enneff 13y ago> defer inhibits refactoring since moving things to function bodies or inlining function bodies silently changes semantics, and cannot be optimized as easily, because of the dynamic aspects. As someone who has written and reviewed hundreds of thousands of lines of Go code, I haven't observed this to be the case in practice. RAII doesn't fit into Go, philosophically, as it lets you trigger hidden functionality on the creation or destruction of data structures, whereas a deferred function can only be run if there's a defer statement there in the code (where you can see it). In Go, the only way to execute a block of code is to make a function call. There are no constructors, destructors, or any other kind of side effect to allocating or deallocating data structures. This brings a huge benefit in terms of readability and transparency. Anyway, I'm not sure why we're comparing defer and RAII, because they're generally used for different purposes.
- pcwalton 13y ago> As someone who has written and reviewed hundreds of thousands of lines of Go code, I haven't observed this to be the case in practice. Sure, a lot of suboptimal design decisions don't cause problems in practice. That doesn't change the fact that they're suboptimal, and in this case lead to worse performance. > RAII doesn't fit into Go, philosophically, as it lets you trigger hidden functionality on the creation or destruction of data structures, whereas a deferred function can only be run if there's a defer statement there in the code (where you can see it). I'm focusing more on RAII as implemented with "scope" in D; whether stuff runs explicitly or implicitly is an orthogonal design choice (although I prefer implicitly running code since you need finalizers anyway in any GC'd language, including Go—so you might as well embrace it). With the "scope" statement you also always have to explicitly call the destructor, but in a lexically scoped way. The main thing I find suboptimal with "defer" is the choice of dynamic mutable state as compared to lexical scoping. > In Go, the only way to execute a block of code is to make a function call. There are no constructors, destructors, or any other kind of side effect to allocating or deallocating data structures. This brings a huge benefit in terms of readability and transparency. http://golang.org/pkg/runtime/#SetFinalizer http://golang.org/pkg/runtime/#SetFinalizer