This is going to be more of a conceptual answer to why these warnings might exist even with try-with-resources approaches. It's also unfortunately not the easy kind of solution you might be hoping to achieve.
Error Recovery Cannot Fail
finally
models a post-transaction control flow that is executed regardless of whether a transaction succeeds or fails.
In the fail case, finally
captures logic that is executed in the middle of recovering from an error, before it has been fully recovered (before we reach our catch
destination).
Imagine the conceptual problem it presents to encounter an error in the middle of recovering from an error.
Imagine a database server where we're trying to commit a transaction, and it fails halfway through (say the server ran out of memory in the middle). Now the server wants to roll back the transaction to a point as though nothing happened. Yet imagine it encounters yet another error in the process of rolling back. Now we end up having a half-committed transaction to the database -- the atomicity and the indivisible nature of the transaction is now broken, and the integrity of the database will now be compromised.
This conceptual problem exists in any language that deals with errors whether it's C with manual error code propagation, or C++ with exceptions and destructors, or Java with exceptions and finally
.
finally
cannot fail in languages that provide it in the same way destructors cannot fail in C++ in the process of encountering exceptions.
The only way to avoid this conceptual and difficult problem is to make sure that the process of rolling back transactions and releasing resources in the middle cannot possibly encounter a recursive exception/error.
So the only safe design here is a design where writer.close()
cannot possibly fail. There are usually ways in the design to avoid scenarios where such things can fail in the middle of recovery, making it impossible.
It's unfortunately the only way -- error recovery cannot fail. The easiest way to ensure this is to make those kinds of "resource release" and "reverse side effects" functions incapable of failing. It's not easy -- proper error recovery is hard and also unfortunately hard to test. But the way to achieve it is to make sure that any functions that "destroy", "close", "shutdown", "roll back", etc. cannot possibly encounter an external error in the process, since such functions will often need to be called in the middle of recovering from an existing error.
Example: Logging
Let's say you want to log stuff inside a finally
block. This is often going to be a huge problem unless logging cannot fail. Logging almost certainly can fail, since it might want to append more data to file, and that can easily find many reasons to fail.
So the solution here is to make it so any logging function used in finally
blocks cannot throw to the caller (it might be able to fail, but it won't throw). How might we do that? If your language allows throwing within the context of finally provided that there's a nested try/catch block, that would be one way to avoid throwing to the caller by swallowing exceptions and turning them into error codes, e.g. Maybe logging can be done in a separate process or thread which can fail separately and outside of an existing error-recovery stack unwind. As long as you can communicate with that process without the possibility of running into an error, that would also be exception-safe, since the safety issue only exists in this scenario if we're recursively throwing from within the same thread.
In this case, we can get away with logging failure provided that it does not throw since failing to log and doing nothing isn't the end of the world (it's not leaking any resources or failing to roll back side effects, e.g.).
Anyway, I'm sure you can already begin to imagine how incredibly difficult it is to truly make a software exception-safe. It might not be necessary to seek this to the fullest degree in all but the most mission-critical software. But it's worth taking note of how to truly achieve exception-safety, since even very general-purpose library authors often fumble here and wreck the whole exception-safety of your application using the library.
SomeFileWriter
If SomeFileWriter
can throw inside close
, then I'd say it's generally incompatible with exception-handling, unless you never try to close it in a context that involves recovering from an existing exception. If the code for it is outside of your control, we might be SOL but it would be worth notifying the authors of this blatant exception-safety issue. If it's within your control, my main recommendation is to make sure that closing it cannot possibly fail by whatever means necessary.
Imagine if an operating system could actually fail to close a file. Now any program that tries to close a file on shut down will fail to shut down. What are we supposed to do now, just keep the application open and in limbo (probably no), just leak the file resource and ignore the problem (maybe okay if it's not so critical)? The safest design: make it so it's impossible to fail to close a file.