Ignoring the throwing of exceptions, these seem to be the common ways of normally exiting a function (one which returns a value, specifically; void functions aren't as interesting) and transferring control back to the caller:
Testimonials:
I prefer 1), though 2b) has advantages too. I like seeing explicitly where the function can exit and what the return value will be in each case. The return statement is a visual marker for this that works particularly well with syntax coloring. I also prefer it when statements take the form of verbs: 'return someValue;' satisfies this, but 'someValue;' looks awkward and alone. The exception is in pure FunctionalProgrammingLanguages, where you don't want statements to look like imperative sentences. For these, 2) is fine. -- JonathanTang
I strongly prefer 3) personally, and tend to write code that fakes that behavior when I don't have it (i.e. all the languages I normally use). In fact, I use that very name "retval" most of the time (or "result" when I feel like writing something that more closely resembles English). -- KarlKnechtel
I feel uneasy about considering what is best without also considering how non-fatal errors are dealt with.
I very strongly prefer 1) as the universal approach to returning values from a function. If the logic of a function concludes that a value has been ascertained then there is no need to do more processing; simply return at that point with the solution value. This can happen in multiple places within a function. The code follows my logic design. -- MartySchrader
I don't think the best ways are covered in the list above, probably because computer languages don't directly support the real need. I would suggest the following:
5) Return a status and a value. Having to return a null, 0, or -1 as a pseudo-value and remember to check for it is highly error prone.
6) Return multiple values. Many operations return more than one value. Why should I have to do programming hand stands to get all the values back?
You can do both of these already, surely? C++ has std::pair and boost::tuple. Python has tuples. You just make it easy to package multiple values together in a single item. For instance for (5), your function returns something of type std::pair<t_StatusValue,t_ReturnValue>. Presumably this can be done in other languages too.
Both of these arguments ignore the basic premise behind the processing of a function: to return a single result. If you need to process and/or return multiple values then you need to pass in and return something other than a simple type. In this case the function's return value can simply indicate success or failure, but the operatives need to be handled a different way.
Concur. The whole idea of a function is, just like in mathematics, to take exactly one parameter and perform some logical translation on it. The result is exactly one product. If you have an operation that affects more than one result then it isn't a function, is it? It's a procedure, and your code design should reflect that.
While it's true that functions in mathematics map exactly one value in the range to exactly one value in the domain, those values can come from the cross product of any well-ordered family of sets. This effectively allows for these functions to take and return multiple values. It should be noted that options 5 and 6 do exactly that.
Quickly:
"Hulk break out! HULK SMASH!!"