Should not, do what is best for the readability of the code
Basically this rule is called Single Entry, Single Exit (SESE) and is an important recommendation in languages that use explicit (or manual) memory management. Especially in languages that do not rely on error handling by exceptions.
It is important to realize that the use of SESE almost always makes code more complex.
In languages like C, for example, when you need to explicitly release resources allocated at the end of the function execution, there is a need to replicate the release of these resources before each return
and there is a risk of confusion when there are several of them. If there is any confusion, the code of the function ends up executing it without making the release or doing improperly.
Resource management and exceptions
In languages that make use of exceptions, SESE already makes even less sense since any function called can potentially generate an output from the currently running function. Even if you can make use of documentation to know if a used function can issue an exception by ending the current function, you have at hand a difficulty understanding the future maintenance code.
In C++, for example, where exceptions exist, it is fundamental to use automatic or semi-automatic resource management through the so-called technique RAII (Resource Acquisition Is Initialization). In Java and C# there are constructions like try-finally
and using
to deal with resource release.
Since the program can have several implicit exit points, by releasing exceptions, where you do not even know where it comes from, it is no use trying to unify the explicit output in a single point. You will create a confusing stream in the code and will not solve any problem.
In languages with exception treatment you have two situations:
- the existence of resources that facilitate the call "clean out" releasing all resources whatever happens;
- the ineffectiveness of the technique since there can always be several invisible exit points in any function called that emits a
throw
or raise
.
Then the SESE technique became unnecessary and even not recommended since in many cases the program flow ends up being complicated in the name of the reliability of the single output.
Unfortunately there are still those who find it important in any language and disseminate information that is partially obsolete, as occurs in many other cases of "good practices" propagated.
To code readability and ease of maintenance are most important a guide that brings no benefit in languages that have their own tools for managing and releasing resources. It is common to have cyclomatic complexity reduced when using several return
making the execution of the function end as soon as possible.
It is evident that make proper use of the return
and maintain a coherent logic is still necessary no matter how often it appears in the body of the function.
Legitimate use
But let’s look at a case where the use of SESE would be legitimate and would solve an existing problem, as is common in language C:
void funcao1() {
resource recurso = acquire_resource(); //talvez possa ser um malloc() aqui
if( funcao2(recurso) )
return; // esqueceu do código de liberação antes do return e o recurso vazou
.
.
.
funcao3(recurso);
release_resource(recurso); //poderia ser um free()
return;
}
With SESE:
void funcao1() {
resource recurso = acquire_resource(); //talvez possa ser um malloc() aqui
if( funcao2(recurso) )
goto saida; // manda para o ponto onde o recurso é liberado e termina a função
.
.
.
funcao3(recurso);
saida:
release_resource(recurso); //poderia ser um free()
return;
}
In languages so you have some options:
Repeat the resource release code
Horrific solution and facilitates the creation of errors, hurts the DRY
Put a goto
for the excerpt of the code that makes the release of resources
The release code must be the last part of the function and is one of the best uses for the goto
Create a local variable to help program flow control
It is far more complicated to control the flow this way than using language controls that hold the state for flow control implicitly.
As these languages bring about these difficulties, the SESE technique was created that requires there to be only one exit point, that is to say a single return
.
It is much easier to standardize this technique. If the programmer has in his head that he should always do this, as if obliged by the language, the chance of him forgetting to make the release is much less.
Completion
We need to use appropriate techniques for each language. Trying to use a technique created for a language in others end up creating the misinterpretations of the type "goto
should never be used" and "comments should be spread throughout the program detailing everything the program does", etc..
Certainly this post does not close the subject and it would be interesting to see additional information. I left a few points out to give other people a chance to complement.
I put in the Github for future reference.
This response is based on the one found in Where Did the notion of "one Return only" come from?
I sometimes consider it better to have only one Return because you can easily debug the result of the method. By checking what comes out in
resultado
in this case (because if not, you will need to place two or more inspections, one for each Return, to see where the method is entering). In compensation this sometimes increases the cyclomatic complexity also.– GarouDan