New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
spec: defer statement specification is easily misread #27802
Comments
CC @griesemer |
My reading of your write-up is that one could reasonably (w/o prior Go knowledge) interpret the sentence "immediately before the surrounding function returns" as "immediately before the surrounding function executes a return statement" - would that be an accurate summary, @seebs ? I think the crucial part here is that that first part of the sentence (which is actually written slightly differently in the spec) is followed by a clarifying 2nd part of the sentence:
It says pretty clearly that (for instance), the surrounding function already executed a return statement, that is the deferred function execution does happen after the return statement. But perhaps, given the subtleties involved here, this is not clear enough. There's also an example to that effect, but the associated comment is perhaps a bit too subdued. Leaving this open for now. It's probably worthwhile being a bit more explicit. |
That's a really good question. I think I would agree that this at least strongly implies that it has to be happening after the return "executed", but I could also see someone inferring that the sequence of events is:
in particular, if you haven't run into the "named return values" special syntax, it's pretty easy to think that the term probably refers to "the values given to the return statement, if they are named objects that the deferred function literals could modify". I did a writeup explaining this (which i eventually plan to do a thing with other than stash it in a repo somewhere) for the benefit of new users: https://github.com/seebs/notes/blob/master/defer_return.md The key thing missing, i think, is the distinction between "the return causes the return values to become set" and "the return causes control flow to transfer back to the caller". If you think of those as a single "return" operation, than defer basically has to happen before the return statement changes the values-that-will-be-returned. (Note, "missing" in the mental model people might have when coming to this, I think the spec basically communicates this, it's just easy to have a mental model which makes it plausibly seem to say something else.) |
@seebs Thanks for the clarification; I agree with the key thing that's missing (per the mental model you are referring to). I think this shouldn't be too hard to make crystal clear in the spec. |
Thanks! Amusingly, the genesis of this is that someone was being confused by this, and I was going to try to write an explanation of why the spec's adequately clear and he just wasn't reading carefully enough, and then after a couple of tries at this I realized that the spec's wording was making it unduly difficult to express the thing without apparently contradicting the spec, or at least requiring a large digression into why what I was saying didn't contradict it. |
Change https://golang.org/cl/136758 mentions this issue: |
What version of Go are you using (
go version
)?1.11 but NA
Does this issue reproduce with the latest release?
Sure.
What operating system and processor architecture are you using (
go env
)?Human brain, some kind of godawful biological monstrosity. "go env" apparently says "i'd like some pizza now".
What did you do?
Tried to explain defer statements to a reasonably competent programmer not familiar with go's spec enough to fully understand the semantics of named return variables.
https://golang.org/ref/spec#Defer_statements
https://play.golang.org/p/qwKgqdXIc4W
(note: I understand and expected this, I'm not saying the language's behavior is wrong)
What did you expect to see?
Slightly clearer spec.
What did you see instead?
The problem here is the spec's phrasing "deferred functions are invoked immediately before the surrounding function returns". Even knowing how it works, I read this as strongly suggesting that the fails() function should work, at least in the case where the return statement is hit; after all, the deferred function should happen immediately before the return.
In fact, deferred functions appear to happen roughly as the function returns. They are clearly after any return statements in the function itself, but they are clearly before stack unwinding. But if I had only the spec, and not experience with the language, I'd expect them to take place before the return, and thus, I'd expect a
return foo
to end up yielding the value of foo as modified by a deferred function.Suggested alternative: "immediately after function execution completes, but before returning control to the caller". The example does make the intent more clear, but it doesn't rule out the expectation that returning a named variable would work the same way.
Similarly, the example's "unlock happens before surrounding function returns" could be misleading. Say my
return
statement uses the locked thing. Does the unlock happening "before" the return make that dangerous? No, but it sounds like it does.The text was updated successfully, but these errors were encountered: