> Guard clause – exit a function early if certain conditions are not met. This makes the rest of the function easier to write (no nested if-statements).
If you have:
if(!isUserPort(strPort)) return;
You're gaining knowledge about input, and then immediately throwing it away (assuming the 'happy' path.)Later when you use strPort, do you check it again? If you pass it to another function, do you check it there too?
"Parse, don't validate" is the name that's been given to the following:
case parsePort strPort of
NotAPort -> ...
RootPort port -> ...
UserPort port -> ...
It's more nested, but now you can pass your UserPort around and not check it too many or too few times.Unless your parsePort returns a specialized type which then can be enforced by the compiler, and all your other APIs use the specialized representation, it's sort of "six of one, half a dozen of the other" situation because if you're throwing your strPort around, you'll be parsing it again and again and again.
And I can achieve what you are showing me just the same by putting my guard clause up the stack, closer to the main program or whatever.
Not all data is so clear cut, your guard clause may be guarding against a numeric struct member whose value is out of range for this one function to operate.
Which is why I usually have a few different wrenches for each size of nut I may encounter. Not dunking on your approach, let a hundred flowers bloom and all that.
People didn't understand that, too vague, hard to enforce. Someone else decided to make it clearer, "try to avoid multiple returns inside a function".
Then someone else comes and decides to tidy up the coding style document, put it in imperative form: "All functions should have a single return statement".
Give it a couple of years, people forgot why the advice was there in the first place and now they just blindly apply an insane rule, teach that to new hires and enforce it in reviews. All functions now look like big arrows and every once in a while the max line length rule has to be relaxed a little to allow for all those levels of indent.
Maybe that's how it entered MISRA-C in the first place.
But your "return" should also have all deallocations performed! Also if you open communications to some external devices which tend to be notoriously stateful, you need to bring them back to a usable state by either completing commands sent so far, or resetting them, or whatever is needed.
If there is nothing analogous to "defer" statements or "finally" clauses, you will be in a world of pain if you sprinkle return statements without paying attention to all details.
Thus I can see a point of such a rule being in MISRA-C or something like it. Guess it's better to enforce an easily checkable rule (prone to birthing monsters) than educate all people properly.
A recommendation likely lifted from one of these sources.
MISRA 2012, Rule 15.5: "Only one exit point should be defined in a function.", advisory - https://www.ibm.com/docs/en/rtr/9.0.0?topic=review-code-misr...
HN users cordenr and bfrog say it was dropped in MISRA 2023: https://news.ycombinator.com/item?id=38680587 and https://news.ycombinator.com/item?id=38704631 . Both in a recent thread on MISRA 2023 at https://news.ycombinator.com/item?id=38674158 .
FWIW, HN user FirmwareBurner in that thread links to the "Embedded System development Coding Reference guide" version 3.0 (2018) from the Software Reliability Enhancement Center, Japan at https://www.ipa.go.jp/publish/qv6pgp00000011mh-att/000065271... which says
M3.1.5:
A function shall end with one return statement.
A return statement to return in the middle of processing shall
be written only in case of recovery from abnormality.Also, that's great news to hear in case I ever want to go back to the automotive industry. :)
You could flex some language lawyering to define "abnormality"...
Here is the only relevant example I found:
p = X_MALLOC(sizeof(*p) * NUM);
if (p == NULL) {
return (MEM_NOTHING);
}
...
X_FREE(p);
return (OK);
It's clearly abnormal.Something like a:
if (get_size(obj) == 0) {
return empty_case;
}
... do more complicated code here ...
return result;
does not feel "abnormal", which are things you likely want to log, given: Logs should be output not only when an abnormal condition
is detected, but also at the timing of, such as, data
communication with an external system.One specific case I have in mind I think it came from the company mostly hiring fresh graduates and seniors indoctrinating them with their blind and badly aged structured programming rules.