It’s funny sometimes how when you’ve done something in a particular way for long enough, it’s easy to forget why you do it the way you do. I’ve been a big fan of early return for a long time and was recently challenged on my view. The argument I had received in favor of single return was dubious to me, but it’s always good to recheck why you do something when you’ve forgotten.
So, while the title is a little hyperbolic, in most common programming languages of today, programming in a single return is decidedly worse than multiple return.
So I did some research as to the reasonings behind the recommendation of single return, and it dates back a bit to Dijkstra and before the days of structured programming, where it was the wild west of GOTO. The full recommendation is that any segment of code should have a single entry point and a single exit.
The notion of a single entry point into a segment of code is just kind of baked in these days with structured programming with objects and classes and the like. But the single vs. multiple exit thing has been contentious in the past and it even came up in comments on a previous post.
Going back to doing some research, the recommendation for single exit boils down to making sure that resources were freed upon exit of the function. If you returned early, you had to make sure you freed whatever had been allocated up until that point, files closed, etc. and if you did it all in one place, it reduced duplication, handled code evolution better and more.
So does the recommendation still hold today? In some cases yes, but in the majority of languages that are in popular use today, no. Specifically, if you’re in a language that has automatic resource management – that is languages with garbage collection, most of the argument goes away right there. But even in resource managed languages, resources sometimes still need to be dealt with explicitly. What about then? Well, if you have a language that has exceptions, then single return doesn’t even work for tracking that.
If you have exceptions, allocated resources must be managed in a try/finally, something akin to Java’s try-with-resources, or whatever the proper idiom in your language is, because anywhere an exception can be thrown is a potential exit point to your function. That is: multiple function exit points come by default.
What about garbage collected languages like Go or a language Rust where they either use multiple return values or a Result or Option? Does single return make sense there? I can give a solid “maybe”. I would say something like deferred return might be appropriate depending on the scope of the resource use within the function, that is: no returns within the scope of the resource use, but outside would be ok. For example, in pseudocode:
1 2 3 4 5 6 7
But if basically all of the function occurs with the file open, then it’s effectively single return.
For languages like C, single return is generally the right way to do
things, depending on context. If the function doesn’t allocate
anything that needs cleanup before function exit, e.g. it could use
alloca, and it never reasonably would allocate anything that need
cleanup, multiple return is just fine. That said, when using
single return for resource cleanup in C, this is one of the “good and
right” uses for GOTO. This way, as resources are acquired, when you
wish to exit the function, you GOTO the relevant part of the cleanup
code as it’s generally a first in, last out affair.
The origin for what started this whole thing for me was about readability. Unnecessary single return is generally less readable than early return. The reason being is that in order to only single return, you wind up with nested if statements where in multiple return they don’t wind up nesting. For example, the following multiple return:
1 2 3 4 5 6 7 8 9
Turns into this in single return:
1 2 3 4 5 6 7 8 9 10 11
Now, as a general rule, I subscribe to the Zen of Python, which among other things states:
Flat is better than nested.
Single return code winds up nesting because the function has to
continue on even though all the actual work is done. Making things
worse is the fact that you’ve introduced an effectively useless
variable, and now the reader, especially if
retVal isn’t declared
final, or whatever the equivalent is in the target language if it
exists, has to mentally evaluate the function to make sure that
retVal isn’t reassigned anywhere, which means they have to read the
rest of the function, which they otherwise might not have to if they
only cared about a specific case. That is: if you are reading for
true, in the multiple return case, you don’t have to read
return bar. In the single return, you have to scan the rest of
the method to see what, if anything, happens to
retVal. In the case
where nothing happens to
retVal save for the final return, you’ve
now wasted your reader’s time looking for something that’s not even
If you’re in a language without exceptions, by all means, single return as you require.