-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: Go 2: deferred code blocks #38520
Comments
So this proposal is simply to avoid |
@mvdan That's not correct I'm afraid since introducing |
Yes, you're adding more ways to write Go code. Which, as I explained, has a high cost - any static analysis tools that currently examine defer statements would need to be updated, for example. This is why the language spec is almost never touched, and even less so for syntax changes. |
Also, please fill https://github.com/golang/proposal/blob/master/go2-language-changes.md. |
Having considered the above document before raising this proposal, I still believe this is a worthy addition to Go. As it stands, this proposal is not:
Whilst I appreciate your concern for the integrity of the language spec, it remains an important issue in any language, not just Go, for the spec to always evolve according to the user base whilst maintaining compatibility with previous versions. Although, if you are still sceptical about this proposal, @mvdan, then your opinions are duly noted. |
For language change proposals, please fill out the template at https://go.googlesource.com/proposal/+/refs/heads/master/go2-language-changes.md . When you are done, please reply to the issue with Thanks! |
I believe Swift's defer operates as proposed. If you want to try it out you can fork my package: https://github.com/rocketlaunchr/igo and make a few adjustments and quickly implement what you are proposing. |
Here's the template that I've been asked to produce: Would you consider yourself a novice, intermediate, or experienced Go programmer? What other languages do you have experience with? Would this change make Go easier or harder to learn, and why? Has this idea, or one like it, been proposed before? If so, how does this proposal differ? Who does this proposal help, and why? What is the proposed change? Please describe as precisely as possible the change to the language. What would change in the language spec? Please also describe the change informally, as in a class teaching Go. defer {
// line 1
// line 2
} Doing it this way means you can now do the error checking inside the defer statement, without having to encapsulate that line in a function call! How awesome is that? Is this change backward compatible? Show example code before and after the change. What is the cost of this proposal? (Every language change has a cost). How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected? Gofmt would need to be amended to format code within the braces accordingly so that it is indented. Goimports is unaffected. With go vet, this proposal if implemented can mean defer codeblocks can be empty ( What is the compile time cost? What is the run time cost? Can you describe a possible implementation? Do you have a prototype? (This is not required.) How would the language spec change? Orthogonality: how does this change interact or overlap with existing features? Is the goal of this change a performance improvement? How would we measure it? Does this affect error handling? Is this about generics? |
What happens if a
That's not semantically equivalent to
The latter is obviously wrong. Is the former wrong? Or does that Not sure if this is a ding for or against this proposal. It's definitely a weird semantic corner. But maybe, just maybe, if we allow it we can get rid of the must-name-return-values-to-update-them-with-defer problem. A similar situation applies to |
@randall77 Good points there. In order to understand the use case of the proposed defer, scope here is also important. The defer function ( |
With the different |
To confirm, do other pending defers for the outer function then still get run? What happens with nested defers? Are they associated with the outer function? We've never had a situation before where you can modify a function's defers while the defers are running. This might pose some implementation challenges. Concretely, this program (https://play.golang.org/p/sVcNwWjb9Cy) prints 0, 1, 2, 3: func main() {
defer func() {
println(3)
}()
defer func() {
println(1)
defer func() {
println(2)
}()
}()
println(0)
} What does this program do? func main() {
defer {
println(3)
}
defer {
println(1)
defer {
println(2)
}
}
println(0)
} |
From Swift 5.2 reference manual: Defer Statement A defer {
statements
} The statements within the If multiple func f() {
defer { print("First defer") }
defer { print("Second defer") }
print("End of function")
}
f()
// Prints "End of function"
// Prints "Second defer"
// Prints "First defer" The statements in the |
What you probably want is not a simple conversion of This then becomes not syntax sugar but a large change to the Go language.
|
What if you'd able to defer statements? Given your example: f, err := os.Create("file.txt")
if err != nil {
panic(err)
}
defer func() {
err := f.Close()
if err != nil {
panic(err)
}
}() It could be: f, err := os.Create("file.txt")
if err != nil {
panic(err)
}
defer if err := f.Close(); err != nil {
panic(err)
} |
The basic idea here is syntactic sugar around the existing |
No further comments. |
What version of Go are you using (
go version
)?What operating system and processor architecture are you using (
go env
)?go env
OutputProposal: Deferred code blocks
Currently in Go, we can defer function calls like this:
However, the problem with this is that we are unable to capture the return values of
f
and use them, so what someone like myself ends up doing is this:Although it works, it's not exactly the best way to go about it; we could call
f()
at the end of the function without using defer then use the results as required.What I thus propose is that we introduce deferred code blocks which will have this syntax:
What this will do is execute the entire code block at the end of the function in which 'defer' is used in. Each line will in the code block will then be run sequentially. To give a more realistic example of where this would be useful:
As opposed to the current way of going about this:
In some respect, you could argue that it's merely cosmetic since all the defer { } is doing is making the otherwise defer func () {}() look neater.
Thoughts?
The text was updated successfully, but these errors were encountered: