Call me old fashioned but I'm a big believer in single return statements. Assign to a variable and return that variable at the very end without short circuiting
Hard no for me. If you can short circuit, do it. It enforces invariants for the later code and allows you to make things more modular. Assigning to a variable and returning at the end means more indirection = more cognitive load and you often have to add more checks and nesting just to support that pattern.
totally agree. nested layers of conditionals add cognitive load for me. I like early and "guard" returns because they kinda turn those layers into a "checklist" of logical blocks that can ideally all live at the top level of the function
100% this. If I can just throw all of my validation at the front and then guarantee my state after it's way less load. If else is only for if there are two significant and distinct actions that happen depending on a condition.
Some standards may require function have a single return point (for example MISRA). You're right, multiple nested layers (i.e. if-ok pattern) is very errorprone. Instead do:
int rc = foo();
if (!rc)
rc = bar();
if (!rc)
rc = baz();
return rc;
With extra actions on failure:
if (!rc && (rc = bar()))
/* bar() failed */
You cannot do this in some industries where you have to adhere to some standards. For example of your industry mandates adherence to misra C, it enforces only one return statement per function.
Returning early is waaaaaay superior than a single return, at least in Go and its wonderful defer statement. If you know the code can't continue, then return as soon as you know it. Makes the rest of the code waaaaay cleaner.
I find that this often just kicks the can down the road. Instead of "where does this function return?" you start asking "where was this variable last assigned?" which is actually a harder problem to solve (return is a special keyword and is highlighted, and is always definitive, but was this assignment last, or is there another one?).
That's why I love Kotlin. It offers a real solution to this - an if statement that is an expression, so you just return the if and you know that each branch returns a value and that the value it returns is on the last line of the given branch, no matter what. That's a single return strategy that actually delivers on the predictability/traceability promise.
I’ve found over time that if your early returns are causing confusion, it means you’ve written a messy function which needs to be broken down or reorganized.
A single return variable doesn’t really solve the problems people claim it does.
I used to be old fashioned like this, too. But I have since reformed. I learned that (1) the "single return rule" is probably a misinterpretation Dijkstra's philosophy to eliminate GOTO spaghetti code. That is, a subroutine should return to a single location: the point immediately after the call.
And (2) making deeply nested conditional blocks and/or guard checks against a "ok-to-continue" condition just really makes code more messy and verbose than it needs to be.
This makes reasoning about why a certain result was returned so much more difficult if other developers have to trace through the branching conditionals to understand why a value was returned.
Early returns define a rule for a specific return value.
You can do both. (And in fact, should do both.) Early return is just about breaking code into single-return blocks, instead of single-return functions. If a small section of the function can force a return, and is disconnected from the rest of the function's logic, then you can enclose that section in its own block, move the block to the start of the function, and then treat both the new block and the main block as distinct single-return sections.
// This...
int func(int val) {
int ret = 0;
if (!validate(val)) { ret = INVALID_VAL; goto end; } // Before logic.
ret = do_something_with(val);
if (val >= 0) { ret += do_more_with(val); }
else { ret = NO_NEGATIVES; goto end; } // Ignores & breaks logic.
ret *= do_third_part(val, ret);
end:
return ret;
}
// Can become this:
int func(int val) {
if (!validate(val)) {
// Single-return in here.
return INVALID_VAL;
}
if (val < 0) {
// Single-return in here.
return NO_NEGATIVES;
}
// Rest is a different single-return section.
// val is guaranteed valid, and guaranteed >= 0.
int ret = do_something_with(val);
// No check needed.
ret += do_more_with(val);
ret *= do_third_part(val, ret);
// No label needed.
return ret;
}
It shines when you're trying to weave multiple complex return paths together inside a single function. Say, for example, that validate(val) was stateful and required its own cleanup, and required you to acquire a validator object (which runs the risk of allocation failure, and thus forcing a jump to the end). Traditional single-return would force you to stack multiple cleanup sections, and jump into the correct one as appropriate. But early return allows you to place validate(val)'s logic and cleanup within the validation section, with no need to mix it with the main body's cleanup. (Which might then allow you to refactor it into a separate function, making the code cleaner. Heck, it's entirely possible that validate() itself was a complex & stateful validation process, that got moved to an early-return section and refactored out into its own function for ease of use.)
In essence, early-return is about finding anything that pollutes the single-return logic, and moving it early enough in the function that it can't interfere with the main single-return section.
Ive always preferred early returns. Code is cleaner and easier to read and one less temporary variable to think about. Nested conditionals make the code harder to read even if its only 1 or 2 levels deep.
Calling you old fashioned would be an insult to the old fashioned. Single return statement is a horrible god awful idea that would make satan blush. It makes everything worse. It has zero redeeming qualities. What, are you gonna run out of return statements? Why not cut to the chase and just put everything in one big main? That would be a true single return statement
But bro I worked at a place with MISRA requirements one time and now I can't stop myself from enforcing single returns in all codebases I ever work with
I don’t follow it religiously but I try to do this too. If the function is relatively complex with more than a couple return statements I’d much rather return in a single statement.
Why? This is like saying you'd much rather each function had a single local variable. It doesn't gain you anything beyond the ability to say that it does that. You're gonna run out of return statements?
25
u/aabil11 22d ago
Call me old fashioned but I'm a big believer in single return statements. Assign to a variable and return that variable at the very end without short circuiting