Article summary
I recently discovered that you can put a @return@ statement in a @finally@ clause in Java and it will still compile. I also discovered that if there is a @return@ statement in a @finally@ clause, it will silently discard an exception if one is raised in the @try@.
Once again, in case that didn’t sink in — a seemingly harmless @return@ in a @finally@ clause will silently discard exceptions and return the specified value!
Now I can’t think of _why_ one would put a @return@ in a @finally@. And I was surprised to find out it is even allowed. This got me wondering if it’s something other languages allow, and if so what happens to exceptions in those languages?
Normal Usage
A @finally@ clause is useful when there is something that needs to happen even in the case of an exception being raised. Closing some kind of I/O is probably one of the most common use cases:
public int getNumber(ConnectionFactory connFactory) throws ConnectionException {
Connection conn = connFactory.createConnection();
try {
return conn.readInteger();
} finally {
conn.close();
}
}
In this example, it’s possible that the @readInteger@ method of the @Connection@ could raise a @ConnectionException@. I can’t do anything to handle the exception at this level so I need it to bubble up to the caller. But the @Connection@ needs to be closed regardless, so putting it in the @finally@ is perfect. Whether or not an exception is raised, the connection will always be closed. And if there is an exception, it will bubble up to the caller.
h2. Java
The expected behavior of a @finally@ block (or at least the behavior I expect) changes when you put a return statement inside of it. In this example (which I tested with Java 1.6) the thrown exception will not bubble out, and the method will return the integer 5.
public int getNumber() {
int result = 0;
try {
result = 5;
throw new RuntimeException("No no no!");
} finally {
return result;
}
}
If the @return result;@ statement is moved outside of the @finally@ clause, the method raises a @RuntimeException@ as I would expect.
h2. JavaScript
It turns out that JavaScript has the exact same behavior — it silently discards exceptions when there is a @return@ statement in a @finally@ clause.
var getNumber = function() {
var result = 0;
try {
result = 5;
throw "No no no!";
} finally {
return result;
}
};
Just like the Java example, this function will alway return the value 5.
h2. Ruby
Surely Ruby doesn’t allow this confusing and error-prone syntax, right? Wrong. If you return from an @ensure@ clause then an exception will be discarded, and a value will be returned.
def get_number
result = 0
begin
result = 5
raise "No no no!"
ensure
return result
end
end
As before, the result of calling this method is going to be 5. The idiom in Ruby is to not use the @return@ statement at all in most cases, so this isn’t likely to affect anyone, but I still found it interesting that it was allowed and had the same results as Java and JavaScript.
h2. C#
Finally some sanity. A C# program will not compile if there is a @return@ in a @finally@. I did not try this myself, but according to this “StackOverflow answer”:http://stackoverflow.com/a/7207369, you will get this compiler error if it is attempted:
bq. Control cannot leave the body of a finally clause
h2. Conclusion
Return statements don’t belong in @finally@ clauses. Surprisingly, to me at least, a number of languages allow this syntax and the non-obvious and nasty side-effect it has of discarding exceptions. A try/finally (or a begin/ensure) can be just what is needed to make sure some code is executed before a method exits – just watch where you put your @return@.
While 99% of the time that will just lead to subtle bugs in error-handling code, having the choice to explicitly return a different value after (and possibly derived from) cleanup code can be useful. It’s a step in the direction of error handling patterns like restartable exceptions, as used in Common Lisp, Smalltalk, and probably some other languages. Unfortunately, the call stack has already been unwound in the languages you list (I think), so there is less information to go on when handling the exception.
You can do roughly the same thing in Lua before the call stack is unwound, but it needs to be explicitly requested with xpcall, so it shouldn’t lead to surprises.