-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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: errors: add Close method for defer io.Closer #40483
Comments
I just want to note that there is often no need to check the result of |
While that may be true for For instance, // Since writes happen asynchronously, Write may return a nil
// error even though the write failed (or will fail). Always
// use the error returned from Writer.Close to determine if
// the upload was successful. As such, // Close completes the write operation and flushes any buffered data. Side note, this is usually a thorn for me because most linters require that |
This is true but makes the linters that check for this like errcheck very noisy as they'll tag the harmless invocations too of which there may now be many. It may still be useful to log the error as there shouldn't be any error. i.e it may be the result of some insidious bug somewhere else. So I'd personally be in favour of something like this to make defer error checking less verbose. And yes you could define such a function in your own code and use it throughout but it does feel like something every Go programmer encounters and as such deserves a spot in the stdlib. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I also think it's highly opinionated, in terms of writing stuff to metric of log dashboards in case an err is non-nil. I realise that this will all still be possible with this proposal, but since it's not a one-size-fits-all, I don't think it should be part of the stdlib. just my 2 cents. |
People do write code that does
but I am not sure I would want to encourage that behavior. Really such code should be wrapping the error with extra context. And to be clear,
is clearly OK for readers. For writers you need to check the result, but often if the result is a failure you want to react to the fact that you didn't actually write the thing you thought you wrote. Hiding the possible error inside a helper (which makes it look like the error is "taken care of") only compounds the problem. It's just unclear how much the library routine would help versus encourage bad code. |
There are two problems I have with the current state of things. First, it is non-trivial to extract an error from a defer: (as defer func() {
if err := f.Close(); err != nil {
log.Println(err)
}
}() Second, after you extract it, if you want to interact with the error (and not just log it as above), you must use a named return and decide which error is more important: func WriteAndClose(w io.WriteCloser, a ...interface{}) (err error) {
defer func() {
if err0 := w.Close(); err == nil && err0 != nil{
err = err0
}
}()
_, err := fmt.Fprint(w, a...)
return err
} |
I completely agree, and somewhat regret posing this as a proposal, since what I want to resolve is the root problem and there are a bunch of ways to do this. Unfortunately, github issues are not a good place to brainstorm. |
These are both reasons not to use defers for code that might fail and influence error handling. So rather than try to fix these problems by adding defer-use-helpers, don't use defer here. |
Not using defer will make the code look even worse, violate line of sight style, and gets exponentially more complex with each additional error) func WriteAndClose(w io.WriteCloser, a ...interface{}) error {
_, err0 := fmt.Fprint(w, a...)
/* 20 lines of other work, none of which can early return */
err10 := w.Close()
if err0 != nil {
return err0
}
/* 9 other errN != nil checks*/
return err10
} |
2c2
< return err
---
> return fmt.Errorf("closing a file: %w", err)
This is demonstrably incorrect. Say I have a reader that, like the gcloud sdk, holds open resources until closure, and the call to
I am more than open to alternatives, this was the best bad option I could come up with. |
Such an implementation of |
The next sentence states that Also, the idea of needing to respond to an error is orthogonal to calling |
@carnott-snap, if an implementation requires behavior that is not safe (or not supported) for In that case, the implementation should take care not to implement the |
Maybe we use the term However, the concept of calling |
Based on the discussion above, this proposal seems like a likely decline. |
No change in consensus, so declined. |
@rsc: since there is a need described in this ticket and you consider my proposed solution unacceptable, can you offer alternatives? Currently there is no ergonomic way to interacted with a deferred error (or io.Closer). |
I don't think there is a single solution to the general issue. |
Then can we drive towards multiple solutions for the major issue types? I understand that closing this proposal may be your way of signaling this this one idea is not accepted, but it also suggests that there is no need for any fix. |
Closing this issue should only suggest that this specific proposal is not accepted. The issue tracker isn't a good place to brainstorm a solution for a problem. It doesn't handle discussions well. I recommend that you use a forum such as golang-nuts. |
description
The
io.Closer
interface presents aClose
method that returns anerror
. Unfortunately the syntax ofdefer
makes it difficult to consume this error:Either for this reason, or because canonically most
io.Closers
never give actionableerror
s, we usually see this:I propose we define a helper function to assist with this:
costs
This would have no costs to the language spec, but would add api surface to the errors package in the standard library. It also requires that the calling function have a named error parameter, which was a caveat in #32437, but this is required for any error handling within a defer, so is more an issue with the language spec. Furthermore, some may find the
errors.Close
interface non-intuitive, but I think there is a win to having a canonical way to do this.alternatives
We could accept that
Close
should be a void method and fix it in Go2. This is going to break a lot of things, and may not be worth the hassle, but it is how the method is usually used. This could also be related to questions about idempotency inio.Closer
, see further reading below.Since this blindly returns the error, and thus bares resemblance to #32437, it may be useful to add a formatter variant. I think that a func variant is overkill since at that point you probably have as much code as the original manual
defer
.Technically it is still possible to ignore the result of
io.Closer.Close
with this implementation, given the function is already are returning an error. We can either pick one, above I select the returned error, as that should have more useful information, but we could sayClose
is more important, though this means that the last deferred close wins. Alternatively, it may be better to conjoin the errors, thougherrors.Unwrap
does not allow multiple inheritance:further reading
#16019
#20705
#25408
#27741
#27750
The text was updated successfully, but these errors were encountered: