It's indefensible that defer works on the function and not on the scope.
Both seem like fair options to me. With function scope, you can func f() { x := ... if x.something() { x.doSomethingEarlier() defer x.cleanup() } // use x however you like } where scope-based forces you to do stuff like func f() { x := ... if x.something() { x.doSomethingEarlier() defer x.cleanup() // use x however you like } else { // use x however you like } } In a scope-based defer, you'd have to keep all related…
More Gotchas of Defer in Go, Part II
41–50 of 55 posts
Re: More Gotchas of Defer in Go, Part II
#42Earlier quoted context omitted.
Both seem like fair options to me. With function scope, you can func f() { x := ... if x.something() { x.doSomethingEarlier() defer x.cleanup() } // use x however you like } where scope-based forces you to do stuff like func f() { x := ... if x.something() { x.doSomethingEarlier() defer x.cleanup() // use x however you like } else { // use x however you like } } In a scope-based defer, you'd have to keep all related…
I think function scope is a good default, and wrapping in an anonymous function and calling it (like your last example) is a simple workaround to get the scope_defer behavior. If it was scope based there's nothing you could do to get func_defer behavior.
That's clearly false. You could set up a list to hold objects to be disposed (or, more generally, closures to execute) and defer a simple procedure that disposes of all objects in the list. This is in fact what the implementation of defer must do internally.
Re: More Gotchas of Defer in Go, Part II
#43Earlier quoted context omitted.
I think function scope is a good default, and wrapping in an anonymous function and calling it (like your last example) is a simple workaround to get the scope_defer behavior. If it was scope based there's nothing you could do to get func_defer behavior.
Yeah, I generally feel the same way. For fairly simple use, scope is more consistent (all scopes / closures are identical), but func is a bit more flexible if you're willing to pay with simple boilerplate. I mean, you can convert them into each other. Scoped can do something like this (go+python blended code 'cuz lazy): func f(){ deferred := [] defer func() { for d in deferred.reverse(): d() }() // plus error handlin…
Re: More Gotchas of Defer in Go, Part II
#44Earlier quoted context omitted.
I have a comment at a higher level with a broader example, but for Go at least this is somewhat common: func f(i interface{}) { if closable, ok := i.(closable); ok { defer closable.close() } // do stuff with i, maybe other casts, etc } There aren't many nice options for "if I can call X, defer a call to X" aside from shoving it into an `if`, where it'd be captured by that scope. I mean, you could do something like de…
Couldn't you do: func closeIfNecessary(object interface{}) { closable, needsClosing := object.(closable) if needsClosing { closable.close() } } And then just do: func f(i interface{}) { defer closeIfNecessary(i) ... } Doing it this way also saves you boilerplate by factoring the downcast out into a separate function.
If you can't call it unless [some other conditions], it goes back to the same kind of problem though. "closable" may not be a good choice on my part, as they're often called unconditionally.
Re: More Gotchas of Defer in Go, Part II
#45Earlier quoted context omitted.
Yeah, I generally feel the same way. For fairly simple use, scope is more consistent (all scopes / closures are identical), but func is a bit more flexible if you're willing to pay with simple boilerplate. I mean, you can convert them into each other. Scoped can do something like this (go+python blended code 'cuz lazy): func f(){ deferred := [] defer func() { for d in deferred.reverse(): d() }() // plus error handlin…
It's more explicit, which is a good thing, as it makes the intent clear. This matters if, for example, the function is later refactored to inline into a caller.
Explicit-all-the-things isn't an unambiguous Good Thing™. If it were, we wouldn't even be discussing this - it's an abstraction, which is less explicit than e.g. building defer out of a list and using GOTO.
Re: More Gotchas of Defer in Go, Part II
#46Earlier quoted context omitted.
I think function scope is a good default, and wrapping in an anonymous function and calling it (like your last example) is a simple workaround to get the scope_defer behavior. If it was scope based there's nothing you could do to get func_defer behavior.
> If it was scope based there's nothing you could do to get func_defer behavior. That's clearly false. You could set up a list to hold objects to be disposed (or, more generally, closures to execute) and defer a simple procedure that disposes of all objects in the list. This is in fact what the implementation of defer must do internally.
Re: More Gotchas of Defer in Go, Part II
#47Re: More Gotchas of Defer in Go, Part II
#48Earlier quoted context omitted.
Go has a garbage collector. RAII is only viable in languages where destruction is predictable, like C++ or PHP.
"defer" is effectively used for predictable destruction in Go, and a more automated RAII scheme could be used in its place. C++/CLI is a language that targets a garbage-collected runtime, yet has full-fledged RAII semantics (they're mapped to CLR Dispose pattern).
Re: More Gotchas of Defer in Go, Part II
#49Defers just feel like a watered down version of what you get with good scoping and RAII. They're a half measure for something programming languages solved decades ago.
Go has a garbage collector. RAII is only viable in languages where destruction is predictable, like C++ or PHP.
Swift and D also have garbage collectors and support RAII.
And since I already see it coming, reference counting is a garbage collection implementation algorithm in computer science literature.
Re: More Gotchas of Defer in Go, Part II
#50#4 can also be fixed with: for i := 0; i
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 an…
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
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.