6 ms·
It's indefensible that defer works on the function and not on the scope.
by br1 9y ago
It's indefensible that defer works on the function and not on the scope.
- willvarfar 9y agoWell I think the function scope the most useful, but wish there was block scope available too. So what I really wish was that languages used dot notation to go up scope eg ..name is that name two blocks out. This was one of the good parts of VB syntax which I miss in other languages. Think how much nicer it is than python's nonlocal and global, for example!
- br1 9y agoIt's true that defer is more powerful at function scope. You can always recover block scope with an unnamed func. But it doesn't fit with normal lexically scoped constructs. You get gotchas. I would love to be able to explicitly affect other scopes as you mention, for example to define two classes at the same time.
- pcwalton 9y agoWhat's the use case for function scoped defer? I have never once needed function scoped RAII in C++ or any other language.
- br1 9y agoCollecting tasks from all iterations to await before returning.
- barsonme 9y agoIt's not terribly uncommon for larger functions to do something like func someSQLStuff() { tx, err := createTx() defer func() { if err != nil { tx.Rollback() log(err) } else { tx.Commit() } }() rows, err = tx.QueryContext( ... ) // more SQL } Basically, function-scoped cleanup. Like closing opened files.
- Groxx 9y agoI 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 deferrable := func(){} if closable { deferrable = closable.close } defer deferrable() but imagine doing that every time. It'd work, sure, but it'd also be more annoying.
- pcwalton 9y agoCouldn'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.
- Groxx 9y agoAs long as you can always call .close() regardless of the code below, yep - that'd work, and is definitely more readable. 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.
- Groxx 9y agoBoth 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 code in the scope, nesting it another layer deeper / possibly duplicating it. On the flip-side is of course that this doesn't work like most would probably want in function-scoped: for i := 0; i < 4; i++ { x := get(i) defer x.cleanup() x.whatever() } and you're forced to for i := 0; i < 4; i++ { x := get(i) func() { defer x.cleanup() x.whatever() }() } I've seen both of these patterns pretty frequently, in Go and in other languages. Go could, of course, have both a func_defer and a scope_defer, but that doesn't seem like it'd fit with the fairly strong focus on keeping the language feeling small and simple. So they had to pick one, and it can't handle both cases.
- infogulch 9y agoI 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.
- Groxx 9y agoYeah, 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 handling if x.something() { deferred.push(func(){ cleanup() }); } // same as func scope } but that's a bit more ridiculous / error-prone (though a helper func is obviously possible) than the equivalent IIFE for func -> scope. More explicit, I suppose, but bleh.