They were wrong and probably not as experienced as they/you thought. Guard clauses make code simpler and more understandable.
http://martinfowler.com/refactoring/catalog/replaceNestedCon...
I think a blanket ban on multiple return locations is silly, as they can often be used to simplify code. There may be times when setting a return value is preferable, and I think you should use your judgement there.
http://stackoverflow.com/questions/36707/should-a-function-h...
if x
return a
return b
if x
return a
else
return b
if x
r = a
else
r = b
return r
Out of these I find the first is the most prone to maintenance errors. It's easy at a glance to see the final return, insert something in front of it, and miss that it needs to happen on another path. At least in the other two cases the indentation makes it clear that it's a conditional return path and you look for others.I don't have a problem with a "throw" instead of "return a" in any of the forms because that's expected to be an aborted path anyway. In the case of two returns, maybe it is, maybe it isn't.
It's a small thing but when you read hundreds of thousands of lines of code, every little thing that makes it easier is worthwhile.
[1] http://martinfowler.com/refactoring/catalog/replaceNestedCon...
However, I happily make exceptions for:
a) Simple shortcut checks at the top of the function. These tend not to increase the complexity of the control flow and can really simplify it.
void free(void * p)
{
if (!p) return;
... rest of function
}
b) Cases where it's just plain unnatural to do it any other way. This can occur with state machines and complex loops.
When I do this, I make sure to put a comment way out on the right. void process_bytes(unsigned char * p)
{
...
for (;;)
{
...
switch (loop_state)
{
...
case specific_case:
switch (input_symbol)
{
...
case end_symbol:
return; //----- note inner return
...
}
break;
...
}
}
}
Before folks jump on me for having nested switch statements or "complex loops" in the first place, let me point out that when I write this type of code it's usually because I'm processing a data format defined by somebody else.On a separate note, my take is that multiple returns are necessary to write readable understandable code quite often. Guard statements (either handling normal simple boundary cases, or throwing exceptions) at the beginning simplify logic and gives a clean reading of the code.
I find that multiple return statements in a function are more often a symptom of ugly code instead of the reason.
if (x): do(thing1) y := do(thing2) if (y): do(thing3) return 0 else: return -1 else: do(thing4) return 0
___
As you can see, such logic could quickly become hard to test and reason about. Does a single return help all that much? Not in and of itself, but it does tend to make writing such code a bit more painful, leading to better designs. However, guard clauses are a superior design in general.
I still avoid multiple returns in my main logic when side effects are involved, at least when I can.
And if the function is not small?
There are times when this transformation yields simpler code, there are times when it makes things more complex, and there are a lot of cases in between where it's a judgment call.