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: return func() from defer for early execution #24349
Comments
You can defer in the middle of a larger function like this: // ...
err := func() error {
file, err := os.Create(foo)
if err != nil {
return err
}
defer file.Close()
if _, err := file.Write(bar); err != nil {
return err
}
return nil
}()
// ... |
This would effectively mean that defers should be expressions instead of statements, which is a pretty substantial change to the language. You'd also lose consistency with go statements, unless you'd make those expressions too. Also note that defers stack, yet your proposal is for one returned func per defer. If you wanted to do multiple defers with the returned funcs, you'd have to add the boilerplate for keeping all of the funcs and calling them. |
@faiface yes and I've used that pattern a couple of times, but it doesn't feel great to be bundling things up in a closure just so I can use defer... |
I often stumble upon the same problem and either split my function into a couple of smaller ones or extract a couple of lines into closure. But I'm not so sure this warrants a change to defer. One could argue that the mere fact that you need that kind of change indicates a problem with the code. And, instead of making elaborate tricks with defer, it would be much clearer to extract a helper routine. Either way, your solution raises a lot of design and implementation questions some of which @mvdan already pointed out. |
To add to @faiface's point of splitting into two funcs or adding an anonymous function; I believe this all comes down to avoiding large blocks with lots of unnecessary variables in the scope. Even if you could "activate" that defer early in the original example, the rest of the block would still have the |
If we write this package: package defer
func Once(f func()) func() {
done := false
return func() {
if !done {
f()
done = true
}
}
} then you can write your code as // ...
file, err := os.Create("/tmp/xy")
if err != nil {
return err
}
close := defer.Once(func() { file.Close() })
defer close()
if _, err := file.Write([]byte("a")); err != nil {
return err
}
if _, err := file.Write([]byte("b")); err != nil {
return err
}
close()
// ... which requires one extra line, and requires an explicit |
That looks like a reasonable suggestion. I'll close this issue. Thanks @ianlancetaylor ! |
I often find I'd like to use
defer
in the middle of a larger function. My options are either to split the functionality out into a smaller function, or duplicate the code I'd like to defer:I propose that defer should return a
func()
. When called, it will execute the deferred code, and prevent it from being executed when the function returns.This will make the code clearer, and would remain backwards compatible:
The text was updated successfully, but these errors were encountered: