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

I don't like calling any of these things stupid. They're tradeoffs, and I can respect why they were made.


I would agree with you in the general case, but I yet to hear any sort of sane explanation for why it is the way - and it is not like a typical tradeoff where something arguably worse would have been chosen otherwise, it is just a small semantic change to a keyword.

But its effect is huge, e.g. the prototypical usage of the keyword would be to unlock mutexes, but it is simply a huge footgun, see: https://hackertimes.com/item?id=30253426


It enables one to conditionally set up cleanup within the function. If it was block scoped, you couldn't do something like `if shouldDoSideEffect { defer something() }`

If it was scope-based, there would be just as many people complaining that it isn't function based.


You could just do `defer if shouldDoSideEffect { something() }`


That's slightly different, though. The first is "when this is true, earlier in the function, queue up running something at the end of the function." This is "at the end of the function, if this is true, do something."

They _should_ be identical, unless shouldDoSideEffect changes.

Yes, mutability is bad. One shouldn't change that. But avoiding "you're holding it wrong" footguns was the original goal, right?

Don't get me wrong, having both would be nice. Maybe something like `after` for function scope and `defer` for block scope. But that starts to erode the "simplicity" of Go, I guess.


I think that gets unwieldy in more complicated cases. You might want to write:

    if condition_A {
        acquire_A()
        defer release_A()
    } else if condition_B() {
        acquire_B()
        defer release_B()
    } else {
        acquire_C()
        defer release_C()
    }
But with block-scoped defer you'd have to write something like:

    if condition_A {
        acquire_A()
    } else if condition_B {
        acquire_B()
    } else {
        acquire_C()
    }
    defer func() {
        if condition_A {
            release_A()
        } else if condition_B {
            release_B()
        } else {
            release_C()
        }
    }()
Maybe the syntax here could be better with some kind of defer-if added to the language, but it's the non-locality of the cleanup that's the real downside. The whole point of the defer keyword is to let you put cleanup right next to resource acquisition, to make code easier to audit and maintain, and we lose that here. (Also this version might not even be correct, if the conditions change their values over time.)

To be honest, I think Rust's combination of destructors and move semantics is really the ideal way of doing cleanup, with C++ coming pretty close too. But given a defer keyword, I can see situations where having it block-scoped would be painful.




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

Search: