← Back to context

Comment by akoboldfrying

1 day ago

defer is no worse than Java's try-with-resources. Neither is true RAII, because in both cases you, the caller, need to remember to write the wordy form ("try (...) {" or "defer ...") instead of the plain form ("..."), which will still compile but silently do the wrong thing.

Sure, true RAII would be improvement over both, but the author's point is that Java is an improvement over Go, because the resource acquisition is lexical scoped, not function-scoped. Imagine if Java's `try (...) { }` didn't clear the resource when the try block ends, but rather when the wrapping method returns. That's how Go's defer works.

  • Can't you create a new block scope in Go? If not, I agree. If so, just do that if you want lexical scoping?

    • defer is not block scoped in Go, it's function scoped. So if you want to defer a mutex unlock it will only be executed at the end of the function even if placed in a block. This means you can't do this (sketch):

        func (f *Foo) foo() {
            // critical section
            {
                 f.mutex.Lock()
                 defer f.mutex.Unlock()
                 // something with the shared resource
            }
            // The lock is still held here, but you probably didn't want that
        }
      

      You can call Unlock directly, but then if there's a panic it won't be unlocked like it would be in the above. That can be an issue if something higher in the call stack prevents the panic from crashing the entire program, it would leave your system in a bad state.

      This is the key problem with defer. It operates a lot like a finally block, but only on function exit which means it's not actually suited to the task.

      And as the sibling pointed out, you could use an anonymous function that's immediately called, but that's just awkward, even if it has become idiomatic.