Multiple Returns on Error

Multiple Returns

This article is about software coding standards and the use of multiple returns within a procedure.

The standard requirement is that there shall be a single return at the end of a procedure.  I make the case to allow for multiple exits on error.

The requirement originated in a time when assembly language code was prevalent.  While modular programming was taking hold, it was easy to jump to from anywhere to any labeled location in any other function.  When a programmer was aware of a nice phrase of code, he could jump in, take advantage of that code, then jump back again.  Often a jump would do the trick and there was no need for the overhead of a call and all the bother with the stack or register saves.

This was a significant problem so the rule became: One entry point, one exit point.  Any given subroutine could be entered only via the designated entry point, and exit via the designated exit point.  The first half of the rule was rightfully welcomed and the second half followed along with little argument. 

In many places this is considered one of the golden rules along others such as Thou shall not use GOTO.  Any code smith arguing with the golden rules is to be chastised greatly and sent back to her or his cubicle in disgrace.  There are times when the rules should be challenged, and possibly changed.

In today’s world of software, we simply cannot jump into the middle of a function (procedure, subroutine, method, or any other preferred name).  But we can jump out.  The argument is still made that multiple exits are confusing.  In general I agree with the rule, but there are exceptions to be made, and they are not that uncommon.  I further hold that exit on error is not confusing, but can significantly reduce complexity.  Reducing complexity and retaining functionality is an ideal condition.

The primary exception to the rule on one entry and one exit is that of exiting on error.  There are times when a function or a segment of code depends on many conditions.  There will sometimes be numerous checks, any one of which can produce a fatal condition.  By fatal, I mean fatal to the function at hand in that the function cannot complete its assigned task.  If the function cannot correct the problem, then an exit is required. 

This exit should be at the soonest possible point.  Often the ideal case is to exit right now.  Not at the end of the module, but as soon as the error is detected. 

However, the rule of only one exit presents a problem.  As an error condition has been detected, we do not want to execute any of the code between our current location and the end of the module.  As the go-to statement is strictly prohibited, our only option is to shroud the remaining code in if statements.


Status = check1( arguments );

If( status == OK )
{
       Status = Operation2( args );
}
Return Status;

If( status == OK )     // shrouding if's repeated for each section
{
       Status = Operation3( args );
}
Return Status;

Aside: Rather than synthesize an elaborate example, I will rely on the reader's experience to recall your own favorite examples.

Often our checks are already complex with many other necessary conditions and options to consider.  We can suddenly find ourselves several levels deep in if statements.  By having to shroud all or many of the function calls and/or code statements in protective if statements, the code becomes obscure. 

The alternative is a simple exit on error.  When a fatal error is discovered, a simple return or return with status is much simpler and easier to understand than a plethora of if statements shrouding the following code.  Particularly when the only purpose of those if statements is to satisfy the rule of one exit.

Sometimes throwing an exception is the proper way.  But I must state, an exception is nothing more than an emergency exit from the middle of the module, and it can be a rather messy one at that.  It may appear simple to the coder, but behind the scenes (and logically above), there can be a lot of messing around with the stack.  The exception may not be caught until several levels up.  There can be more discussions about dealing with exception and the sudden change in operating environment, but that is another topic. 

If the error does not warrant the drastic measure of an exception with all the attendant TRY and CATCH code, a simple return or return( status ) works much better.

When using the philosophy of return on error, I can write each line of code with the knowledge that the status is good.  Major errors have been detected and handled and I can jump right into the needed activity without shrouding my code with if statements that have no purpose other than providing for a single exit point.


Status = check1( arguments );
If( status != OK )
{
Return Status;
}

Status = Operation2( args );
If( status != OK )
{
status = fix_problem( args );
}

   Status = Operation3( args );

Return Status;


In some cases we can simplify the code with a slightly more complex if statement.


if( ( status = Operation4( args ) ) != STATUS_OK ) return( status );

But this can be fraught with danger. Complex if statements merit a topic all their own. For now, allow only one function in an if statement. If you need more, resolve all evaluations to simple values then put those simplified values in an if statement. (Just don’t forget to think positive.)

Obviously there can be variations that return something more meaningful than true or false.  Again, I refrain from synthesizing some messy code with the knowledge you can provide your own.  If you would like to contribute an example, I may include it in these pages or link to it.  Unless noted otherwise, credit will be given.

There will be some who feel like they are violating sacred rules if they allow this.  Some will say that multiple exits confuse the reader.  I strongly disagree.  Exit on error simplifies the code and makes it much easer to ready.  I can directly see what the code intends to do without having to look through shrouding if statements.

In summary, when you get into a finicky or messy situation with a numerous conditions and checks, consider the use of exit on error.  I believe you will find many cases where it simplifies your code.  As with many rules and coding patterns that we all use, overuse is not appropriate.  Do not (sic) take great care to avoid overuse, but then again, use only when needed.  Your code will be better.