go: proposal: Go 2: deferred code blocks
What version of Go are you using (go version)?
$ go version go version go1.14.2 linux/amd64
What operating system and processor architecture are you using (go env)?
go env Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/home/damien/.cache/go-build" GOENV="/home/damien/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/home/damien/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/lib/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build816314905=/tmp/go-build -gno-record-gcc-switches"
Proposal: Deferred code blocks
Currently in Go, we can defer function calls like this:
defer f()
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:
defer func() {
result := f()
// do something with result
}()
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:
defer {
// do this at the end of the enclosing function
}
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:
f, err := os.Create("file.txt")
if err != nil {
panic(err)
}
defer {
err := f.Close()
if err != nil {
panic(err)
}
}
As opposed to the current way of going about this:
f, err := os.Create("file.txt")
if err != nil {
panic(err)
}
defer func() {
err := f.Close()
if err != nil {
panic(err)
}
}()
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?
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 36
- Comments: 17 (12 by maintainers)
So this proposal is simply to avoid
func()and()? Personally, it doesn’t seem worth the high cost that all language syntax changes come with. Also,deferhas a nice consistency now, just likego, where it’s always followed by a function call. You would break that consistency, and thus break lots of tools too.With the different
returnsemantics, this is no longer just a cosmetic proposal.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.
The basic idea here is syntactic sugar around the existing
deferstatement. But as others have pointed out there are complexities if the newdeferblock uses areturnor agotoout of the block. Also, the emoji voting is not in favor. Therefore, this is a likely decline. Leaving open for three weeks for final comments.What if you’d able to defer statements?
Given your example:
It could be:
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.