The classical way to program is with try ... catch
. When is it appropriate to use try
without catch
?
In Python the following appears legal and can make sense:
try:
#do work
finally:
#do something unconditional
However, the code didn't catch
anything. Similarly one could think in Java it would be as follows:
try {
//for example try to get a database connection
}
finally {
//closeConnection(connection)
}
It looks good and suddenly I don't have to worry about exception types, etc. If this is good practice, when is it good practice? Alternatively, what are the reasons why this is not good practice or not legal? (I didn't compile the source. I'm asking about it as it could be a syntax error for Java. I checked that the Python surely compiles.)
A related problem I've run into is this: I continue writing the function/method, at the end of which it must return something. However, it may be in a place which should not be reached and must be a return point. So, even if I handle the exceptions above, I'm still returning NULL
or an empty string at some point in the code which should not be reached, often the end of the method/function. I've always managed to restructure the code so that it doesn't have to return NULL
, since that absolutely appears to look like less than good practice.
9 Answers 9
It depends on whether you can deal with the exceptions that can be raised at this point or not.
If you can handle the exceptions locally you should, and it is better to handle the error as close to where it is raised as possible.
If you can't handle them locally then just having a try / finally
block is perfectly reasonable - assuming there's some code you need to execute regardless of whether the method succeeded or not. For example (from Neil's comment), opening a stream and then passing that stream to an inner method to be loaded is an excellent example of when you'd need try { } finally { }
, using the finally clause to ensure that the stream is closed regardless of the success or failure of the read.
However, you will still need an exception handler somewhere in your code - unless you want your application to crash completely of course. It depends on the architecture of your application exactly where that handler is.
-
11"and it is better to handle the error as close to where it is raised as possible." eh, it depends. If you can recover and still complete the mission (so to speak), yes of course. If you cannot, better to let the exception go all the way to the top, where (likely) user intervention will be required to deal with what happened.Ripped Off– Ripped Off2012年05月02日 12:59:50 +00:00Commented May 2, 2012 at 12:59
-
17@will - that's why I used the phrase "as possible".ChrisF– ChrisF2012年05月02日 13:00:56 +00:00Commented May 2, 2012 at 13:00
-
10Good answer, but I would add an example: Opening a stream and passing that stream to an inner method to be loaded is an excellent example of when you'd need
try { } finally { }
, taking advantage of finally clause to ensure stream is ultimately closed regardless of success/failure.Neil– Neil2012年10月24日 09:35:35 +00:00Commented Oct 24, 2012 at 9:35 -
because sometimes all the way on top is as close as one can doNewtopian– Newtopian2015年01月27日 16:46:44 +00:00Commented Jan 27, 2015 at 16:46
-
1"just having a try / finally block is perfectly reasonable " was looking exactly for this answer. thank you @ChrisFneal aise– neal aise2015年06月24日 12:07:46 +00:00Commented Jun 24, 2015 at 12:07
The finally
block is used for code that must always run, whether an error condition (exception) occurred or not.
The code in the finally
block is run after the try
block completes and, if a caught exception occurred, after the corresponding catch
block completes. It is always run, even if an uncaught exception occurred in the try
or catch
block.
The finally
block is typically used for closing files, network connections, etc. that were opened in the try
block. The reason is that the file or network connection must be closed, whether the operation using that file or network connection succeeded or whether it failed.
Care should be taken in the finally
block to ensure that it does not itself throw an exception. For example, be doubly sure to check all variables for null
, etc.
-
9+1: It's idiomatic for "must be cleaned up". Most uses of
try-finally
can be replaced with awith
statement.S.Lott– S.Lott2012年01月23日 18:07:45 +00:00Commented Jan 23, 2012 at 18:07 -
13Various languages have extremely useful language-specific enhancements to the
try/finally
construct. C# hasusing
, Python haswith
, etc.yfeldblum– yfeldblum2012年01月23日 18:16:26 +00:00Commented Jan 23, 2012 at 18:16 -
5@yfeldblum - there is a subtle diff between
using
andtry-finally
, as theDispose
method won't be called by theusing
block if an exception happens in theIDisposable
object's constructor.try-finally
allows you to execute code even if the object's constructor throws an exception.Scott Whitlock– Scott Whitlock2012年02月15日 16:27:48 +00:00Commented Feb 15, 2012 at 16:27 -
5@ScottWhitlock: That's a good thing? What are you trying to do, call a method on an unconstructed object? That's a billion kinds of bad.DeadMG– DeadMG2012年02月15日 16:55:20 +00:00Commented Feb 15, 2012 at 16:55
-
1@DeadMG: See also stackoverflow.com/q/14830534/18192Brian– Brian2013年04月25日 03:21:03 +00:00Commented Apr 25, 2013 at 3:21
An example where try... finally without a catch clause is appropriate (and even more, idiomatic) in Java is usage of Lock in concurrent utilities locks package.
- Here's how it is explained and justified in API documentation (bold font in quote is mine):
...The absence of block-structured locking removes the automatic release of locks that occurs with synchronized methods and statements. In most cases, the following idiom should be used:
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
When locking and unlocking occur in different scopes, care must be taken to ensure that all code that is executed while the lock is held is protected by try-finally or try-catch to ensure that the lock is released when necessary.
-
Can I put
l.lock()
inside try?try{ l.lock(); }finally{l.unlock();}
RMachnik– RMachnik2015年06月25日 18:55:37 +00:00Commented Jun 25, 2015 at 18:55 -
1technically, you can. I didn't put it there because semantically, it makes less sense.
try
in this snippet is intended to wrap resource access, why polluting it with something unrelated to thatgnat– gnat2015年06月25日 18:58:22 +00:00Commented Jun 25, 2015 at 18:58 -
4You can, but if
l.lock()
fails, thefinally
block will still run ifl.lock()
is inside thetry
block. If you do it like gnat suggests, thefinally
block will only run when we know that the lock was acquired.Wtrmute– Wtrmute2016年03月04日 16:32:28 +00:00Commented Mar 4, 2016 at 16:32
At a basic level catch
and finally
solve two related but different problems:
catch
is used to handle a problem that was reported by code you calledfinally
is used to clean up data/resources that the current code created/modified, no matter if a problem occurred or not
So both are related somehow to problems (exceptions), but that's pretty much all they have in common.
An important difference is that the finally
block must be in the same method where the resources got created (to avoid resource leaks) and can't be put on a different level in the call stack.
The catch
however is a different matter: the correct place for it depends on where you can actually handle the exception. There's no use in catching an exception at a place where you can do nothing about it, therefore it's sometimes better to simply let it fall through.
-
2Nitpick: "... the finally block must be in the same method where the resources got created ... ". It is certainly a good idea to do it that way, because it is easier to see there is no resource leak. However, it is not a necessary precondition; i.e. you don't have to do it that way. You can release the resource in the
finally
of a (statically or dynamically) enclosing try statement ... and still be 100% leak-proof.Stephen C– Stephen C2013年04月25日 04:32:47 +00:00Commented Apr 25, 2013 at 4:32
In many languages a finally
statement also runs after the return statement. This means you can do something like:
try {
// Do processing
return result;
} finally {
// Release resources
}
Which releases the resources regardless of how the method was ended with an exception or a regular return statement.
Whether this is good or bad is up for debate, but try {} finally {}
is not always limited to exception handling.
@yfeldblum has the correct answer: try-finally without a catch statement should usually be replaced with an appropriate language construct.
In C++, it's using RAII and constructors/destructors; in Python it's a with
statement; and in C#, it's a using
statement.
These are nearly always more elegant because the initialization and finalization code are in one place (the abstracted object) rather than in two places.
-
2And in Java it's ARM blocksMarkJ– MarkJ2012年02月15日 15:33:28 +00:00Commented Feb 15, 2012 at 15:33
-
2Supposing you have a badly designed object (For instance, one which doesn't appropriately implement IDisposable in C#) that isn't always a viable option.mootinator– mootinator2012年02月15日 19:00:12 +00:00Commented Feb 15, 2012 at 19:00
-
1@mootinator: can't you inherit from the badly designed object and fix it?Neil G– Neil G2012年02月15日 22:40:05 +00:00Commented Feb 15, 2012 at 22:40
-
2Or encapsulation? Bah. I mean yes, of course.mootinator– mootinator2012年02月16日 15:26:41 +00:00Commented Feb 16, 2012 at 15:26
I might invoke the wrath of Pythonistas (don't know as I don't use Python much) or programmers from other languages with this answer, but in my opinion most functions should not have a catch
block, ideally speaking. To show why, let me contrast this to manual error code propagation of the kind I had to do when working with Turbo C in the late 80s and early 90s.
So let's say we have a function to load an image or something like that in response to a user selecting an image file to load, and this is written in C and assembly:
I omitted some low-level functions but we can see that I've identified different categories of functions, color-coded, based on what responsibilities they have with respect to error-handling.
Point of Failure and Recovery
Now it was never hard to write the categories of functions I call the "possible point of failures" (the ones that throw
, i.e.) and the "error recovery and report" functions (the ones that catch
, i.e.).
Those functions were always trivial to write correctly before exception handling was available since a function that can run into an external failure, like failing to allocate memory, can just return a NULL
or 0
or -1
or set a global error code or something to this effect. And error recovery/reporting was always easy since once you worked your way down the call stack to a point where it made sense to recover and report failures, you just take the error code and/or message and report it to the user. And naturally a function at the leaf of this hierarchy which can never, ever fail no matter how it's changed in the future (Convert Pixel
) is dead simple to write correctly (at least with respect to error handling).
Error Propagation
However, the tedious functions prone to human error were the error propagators, the ones that didn't directly run into failure but called functions that could fail somewhere deeper in the hierarchy. At that point, Allocate Scanline
might have to handle a failure from malloc
and then return an error down to Convert Scanlines
, then Convert Scanlines
would have to check for that error and pass it down to Decompress Image
, then Decompress Image->Parse Image
, and Parse Image->Load Image
, and Load Image
to the user-end command where the error is finally reported.
This is where a lot of humans make mistakes since it only takes one error propagator to fail to check for and pass down the error for the entire hierarchy of functions to come toppling down when it comes to properly handling the error.
Further, if error codes are returned by functions, we pretty much lose the ability in, say, 90% of our codebase, to return values of interest on success since so many functions would have to reserve their return value for returning an error code on failure.
Reducing Human Error: Global Error Codes
So how can we reduce the possibility of human error? Here I might even invoke the wrath of some C programmers, but an immediate improvement in my opinion is to use global error codes, like OpenGL with glGetError
. This at least frees the functions to return meaningful values of interest on success. There are ways to make this thread-safe and efficient where the error code is localized to a thread.
There are also some cases where a function might run into an error but it's relatively harmless for it to keep going a little bit longer before it returns prematurely as a result of discovering a previous error. This allows for such a thing to happen without having to check for errors against 90% of function calls made in every single function, so it can still allow proper error handling without being so meticulous.
Reducing Human Error: Exception-Handling
However, the above solution still requires so many functions to deal with the control flow aspect of manual error propagation, even if it might have reduced the number of lines of manual if error happened, return error
type of code. It wouldn't eliminate it completely since there would still often need to be at least one place checking for an error and returning for almost every single error propagation function. So this is when exception-handling comes into the picture to save the day (sorta).
But the value of exception-handling here is to free the need for dealing with the control flow aspect of manual error propagation. That means its value is tied to the ability to avoid having to write a boatload of catch
blocks throughout your codebase. In the above diagram, the only place that should have to have a catch
block is the Load Image User Command
where the error is reported. Nothing else should ideally have to catch
anything because otherwise it's starting to get as tedious and as error-prone as error code handling.
So if you ask me, if you have a codebase that really benefits from exception-handling in an elegant way, it should have the minimum number of catch
blocks (by minimum I don't mean zero, but more like one for every unique high-end user operation that could fail, and possibly even fewer if all high-end user operations are invoked through a central command system).
Resource Cleanup
However, exception-handling only solves the need to avoid manually dealing with the control flow aspects of error propagation in exceptional paths separate from normal flows of execution. Often a function which serves as an error propagator, even if it does this automatically now with EH, might still acquire some resources it needs to destroy. For example, such a function might open a temporary file it needs to close before returning from the function no matter what, or lock a mutex it needs to unlock no matter what.
For this, I might invoke the wrath of a lot of programmers from all sorts of languages, but I think the C++ approach to this is ideal. The language introduces destructors which get invoked in a deterministic fashion the instant an object goes out of scope. Because of this, C++ code which, say, locks a mutex through a scoped mutex object with a destructor need not manually unlock it, since it will be automatically unlocked once the object goes out of scope no matter what happens (even if an exception is encountered). So there's really no need for well-written C++ code to ever have to deal with local resource cleanup.
In languages that lack destructors, they might need to use a finally
block to manually clean up local resources. That said, it still beats having to litter your code with manual error propagation provided you don't have to catch
exceptions all over the freaking place.
Reversing External Side Effects
This is the most difficult conceptual problem to solve. If any function, whether it's an error propagator or point of failure causes external side effects, then it needs to roll back or "undo" those side effects to return the system back into a state as though the operation never occurred, instead of a "half-valid" state where the operation halfway succeeded. I know of no languages that make this conceptual problem much easier except languages that simply reduce the need for most functions to cause external side effects in the first place, like functional languages which revolve around immutability and persistent data structures.
Here finally
is arguably the among the most elegant solutions out there to the problem in languages revolving around mutability and side effects, because often this type of logic is very specific to a particular function and doesn't map so well to the concept of "resource cleanup". And I recommend using finally
liberally in these cases to make sure your function reverses side effects in languages that support it, regardless of whether or not you need a catch
block (and again, if you ask me, well-written code should have the minimum number of catch
blocks, and all catch
blocks should be in places where it makes the most sense as with the diagram above in Load Image User Command
).
Dream Language
However, IMO finally
is close to ideal for side effect reversal but not quite. We need to introduce one boolean
variable to effectively roll back side effects in the case of a premature exit (from a thrown exception or otherwise), like so:
bool finished = false;
try
{
// Cause external side effects.
...
// Indicate that all the external side effects were
// made successfully.
finished = true;
}
finally
{
// If the function prematurely exited before finishing
// causing all of its side effects, whether as a result of
// an early 'return' statement or an exception, undo the
// side effects.
if (!finished)
{
// Undo side effects.
...
}
}
If I could ever design a language, my dream way of solving this problem would be like this to automate the above code:
transaction
{
// Cause external side effects.
...
}
rollback
{
// This block is only executed if the above 'transaction'
// block didn't reach its end, either as a result of a premature
// 'return' or an exception.
// Undo side effects.
...
}
... with destructors to automate cleanup of local resources, making it so we only need transaction
, rollback
, and catch
(though I might still want to add finally
for, say, working with C resources that don't clean themselves up). However, finally
with a boolean
variable is the closest thing to making this straightforward that I've found so far lacking my dream language. The second most straightforward solution I've found for this is scope guards in languages like C++ and D, but I always found scope guards a little bit awkward conceptually since it blurs the idea of "resource cleanup" and "side effect reversal". In my opinion those are very distinct ideas to be tackled in a different way.
My little pipe dream of a language would also revolve heavily around immutability and persistent data structures to make it much easier, though not required, to write efficient functions that don't have to deep copy massive data structures in their entirety even though the function causes no side effects.
Conclusion
So anyway, with my ramblings aside, I think your try/finally
code for closing the socket is fine and great considering that Python doesn't have the C++ equivalent of destructors, and I personally think you should use that liberally for places that need to reverse side effects and minimize the number of places where you have to catch
to places where it makes the most sense.
-
1Too bad this user disappered. If most answers held this standard, SO would be better off for it.oligofren– oligofren2020年11月21日 14:07:07 +00:00Commented Nov 21, 2020 at 14:07
As several other answers do a good job of explaining, try ... finally ...
is indeed good practice in some situations. Here I want to point out that Python language itself gives you a strong hint that it is by giving you the with
statement.
As the documentation points out, a with
statement is semantically equivalent to a try ... except ... finally ...
block. The __exit__()
routine that is part of the context manager is always called when the block is completed (it's passed exception information if any exception occurred) and is expected to do cleanup. While it's possible also to handle exceptions at this point, it's quite normal always to let higher levels deal with the exception, and the API makes this easy:
If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.
Neil G suggests that try ... finally ...
should always be replaced with a with
. I disagree: which you should use depends on whether in that particular situation you feel that readers of your code would be better off seeing the cleanup code right there, or if it's more readable with the cleanup hidden in a an __exit__()
method in the context manager object. For frequently-repeated situations where the cleanup is obvious, such as with open('somefile') as f: ...
, with
works better. For rarer situations where you're doing a more unusual cleanup (say, by deleting a file you failed to write completely) it may be better to have that stated explicitly at the call site.
Catching errors/exception and handling them in a neat manner is highly recommended even if not mandatory.
The reason I say this is because I believe every developer should know and tackle the behavior of his/her application otherwise he hasn't completed his job in a duly manner. There is no situation for which a try-finally block supersedes the try-catch-finally block.
I will give you a simple example: Assume that you have written the code for uploading files on the server without catching exceptions. Now, if for some reason the upload fails, the client will never know what went wrong. But, if you have caught the exception, you can display a neat error message explaining what went wrong and how can the user remedy it.
Golden rule: Always catch exception, because guessing takes time
-
26-1: In Java, a finally clause may be needed to release resources (e.g. close a file or release a DB connection). That is independent of the ability to handle an exception.kevin cline– kevin cline2012年01月23日 18:47:45 +00:00Commented Jan 23, 2012 at 18:47
-
@kevincline, He is not asking whether to use finally or not...All he is asking is whether catching an exception is required or not....He knows what try , catch and finally does.....Finally is the most essential part, we all know that and why it's used....Pankaj Upadhyay– Pankaj Upadhyay2012年01月23日 18:52:33 +00:00Commented Jan 23, 2012 at 18:52
-
67@Pankaj: your answer suggests that a
catch
clause should always be present whenever there is atry
. More experienced contributors, including me, believe this is poor advice. Your reasoning is flawed. The method containing thetry
is not the only possible place the exception can be caught. It is often simplest and best to allow catch and report exceptions from the top-most level, rather than duplicating catch clauses throughout the code.kevin cline– kevin cline2012年01月23日 19:25:27 +00:00Commented Jan 23, 2012 at 19:25 -
2With exceptions, you want the normal execution of statements to be interrupted (and without manually checking for success at each step). This is especially the case with deeply nested method calls - a method 4 layers inside some library can't just "swallow" an exception; it needs to be thrown back out through all the layers. Of course, each layer could wrap the exception and add additional info; this is often not done for various reasons, additional time to develop and unnecessary verbosity being the two biggest.Daniel B– Daniel B2012年09月18日 11:42:38 +00:00Commented Sep 18, 2012 at 11:42
-
4This is actively bad advice. Never catch an exception unless you are prepared to deal with the underlying failure or at least re-throw it. The absolute worst codebase I have ever had to fix contained hundreds of idiotic placebo log-and-quash catch blocks peppered all across it that did nothing for user experience but leave corrupted state for other code to crash on, while actively preventing higher-level code from detecting and recovering from the failures. Crashing immediately is a more correct behavior than blindly quashing exceptions.AJMansfield– AJMansfield2021年03月05日 20:41:36 +00:00Commented Mar 5, 2021 at 20:41
try/catch
is not "the classical way to program." It's the classical C++ way to program, because C++ lacks a proper try/finally construct, which means you have to implement guaranteed reversible state changes using ugly hacks involving RAII. But decent OO languages don't have that problem, because they provide try/finally. It's used for a very different purpose than try/catch.