Surely this would all go away if Go had an exception handling mechanism like most mainstream languages do? You'd just concentrate on the "happy path", you'd close the file, there'd be nothing to forget or write blog posts about because the exception would be propagated, without needing to write any lines of code.
This problem isn't solved with exceptions either. The problem is that finalizers (C++ destructors, Java's `finally` blocks, Go's `defer` etc.) shouldn't fail but `close()` can fail. Therefore, for 100% correctness, `close()` calls should be handled explicitly and not left to finalizers. Finalizers shouldn't fail because they might be executed while another exception is already in flight. Three languages have three di…
Don't defer Close() on writable files (2017)
41–50 of 311 posts
Re: Don't defer Close() on writable files (2017)
#42Earlier quoted context omitted.
This problem isn't solved with exceptions either. The problem is that finalizers (C++ destructors, Java's `finally` blocks, Go's `defer` etc.) shouldn't fail but `close()` can fail. Therefore, for 100% correctness, `close()` calls should be handled explicitly and not left to finalizers. Finalizers shouldn't fail because they might be executed while another exception is already in flight. Three languages have three di…
> in my opinion they all do the wrong thing What would be the right thing? Combining the original exception and the error from `close` into some kind of `MultipleError`?
Re: Don't defer Close() on writable files (2017)
#43Earlier quoted context omitted.
> Explicit error handling is a choice Yeah, it is - a bad choice IMO. I know the "if err != nil" pattern is spoken up as some sort of cultural idiosyncrasy of Go, similar to the whitespace formatting in python. But so far, I haven't seen any actual data (or even arguments) why it is superior to exceptions, or which inherent problems of exceptions it solves. (The classical example of "it makes control flow more obviou…
Well, let's assume Go did commonly make use of exception handlers for error cases: defer func() { if r := recover(); r != nil { fmt.Println("Failed to write file", r) } }() f := os.Create("file") defer f.Close() io.WriteString(f, "Hello, World!") Cool. You've solved one problem layer, perhaps. But if you look closely you'll notice that code still has a bug! So clearly exception handlers aren't enough. There might be…
Do you mean the issue that if both WriteString() and Close() throw an exception, the one from WriteString() will be swallowed?
That used to be a problem, but has been solved in more modern implementations using suppressed exception tracking.
Re: Don't defer Close() on writable files (2017)
#44Surely this would all go away if Go had an exception handling mechanism like most mainstream languages do? You'd just concentrate on the "happy path", you'd close the file, there'd be nothing to forget or write blog posts about because the exception would be propagated, without needing to write any lines of code.
Go does indeed has an exception handling system like most other languages. Errors and exceptions are very different things, though. Of course, nothing stops you from building your own file handing package that overloads exception handlers to deal with errors. If it gains traction then it would prove the stdlib should consider a v2 API. But that you already haven’t done so is telling…
So it is telling. But I think what it actually tells is that people would have done it just use another language instead.
Re: Don't defer Close() on writable files (2017)
#45Earlier quoted context omitted.
> Explicit error handling is a choice Yeah, it is - a bad choice IMO. I know the "if err != nil" pattern is spoken up as some sort of cultural idiosyncrasy of Go, similar to the whitespace formatting in python. But so far, I haven't seen any actual data (or even arguments) why it is superior to exceptions, or which inherent problems of exceptions it solves. (The classical example of "it makes control flow more obviou…
> I haven't seen any actual data (or even arguments) why it is superior to exceptions, or which inherent problems of exceptions it solves It's faster. Doing all error handling via exception is not viable if you want speed. Exceptions work well for errors in the sense that these rarely happen. But using them for general "this is the bad outcome" of an operation that can happen in the hot-path is problematic. For examp…
(As for "handling" errors, you should only have a few places in the entire codebase which do that... littering the entire codebase with opportunities to feel like you might could handle an error seems like a mistake as it just encourages more handling.)
Re: Don't defer Close() on writable files (2017)
#46Earlier quoted context omitted.
Well, let's assume Go did commonly make use of exception handlers for error cases: defer func() { if r := recover(); r != nil { fmt.Println("Failed to write file", r) } }() f := os.Create("file") defer f.Close() io.WriteString(f, "Hello, World!") Cool. You've solved one problem layer, perhaps. But if you look closely you'll notice that code still has a bug! So clearly exception handlers aren't enough. There might be…
Maybe I'm too dumb too see the bug here. Do you mean the issue that if both WriteString() and Close() throw an exception, the one from WriteString() will be swallowed? That used to be a problem, but has been solved in more modern implementations using suppressed exception tracking.
Think more carefully about what we're actually trying to solve. It is not just about errors.
Re: Don't defer Close() on writable files (2017)
#47Re: Don't defer Close() on writable files (2017)
#48Earlier quoted context omitted.
You mean panics? They don't work across go routines so they're limited and hardly exception handling like most other languages
Which languages see exception handlers work across go routines? Anyway, most don't. There is no difference from exception handlers in most other languages. The syntax is a little different. Is that where you've become confused?
Are there actually ANY languages other than go that have coroutines, and try/catch/throw mechanisms, where you cannot throw across a coroutine boundary?
And why would exception handlers NOT work across coroutine boundaries, other than laziness on the part of implementers?
Re: Don't defer Close() on writable files (2017)
#49Earlier quoted context omitted.
This problem isn't solved with exceptions either. The problem is that finalizers (C++ destructors, Java's `finally` blocks, Go's `defer` etc.) shouldn't fail but `close()` can fail. Therefore, for 100% correctness, `close()` calls should be handled explicitly and not left to finalizers. Finalizers shouldn't fail because they might be executed while another exception is already in flight. Three languages have three di…
> in my opinion they all do the wrong thing What would be the right thing? Combining the original exception and the error from `close` into some kind of `MultipleError`?
I don't thing there's one true right thing™ though. That's why explicit handling is necessary: The compiler doesn't have enough context to handle it for you. The programmer needs to decide what's the right way to handle it.
Re: Don't defer Close() on writable files (2017)
#50Here is my favorite solution to this problem // CheckClose is a utility function used to check the return from // Close in a defer statement. func CheckClose(c io.Closer, err *error) { cerr := c.Close() if *err == nil { *err = cerr } } Use like this - you must name the error return func whatever() (err error) { f, err := os.Open(blah) // ... defer CheckClose(f, &err) // ... } This closes the file and if there wasn't…
Which of course isn't really a "solution", as it results in ambiguous error semantics; this is especially the case with Go's defer, as it pushes the code to the end of the entire function, not merely some intermediate relevant scope... the result is that a file used near the beginning of a function might fail to close but that error will not be realized until after something else in the function fails, after the poin…
If you need "strict" requirements then it is likely impossible but you can get close enough if you use sqlite. Or more lightweight atomic/thread-safe/fault-tolerant file library. You could rollout your own: it easy to start, and continue until the error rate is tolerable for your application (though it may take more dev time).
If you don't need db-like strict guarantees. Just write your app knowing that it may fail (data may be lost, corrupted). It may be ok in a lot of cases.