3

Lets say I have a function that takes an argument, does some action based on the value of that argument and returns false if there is no action for that value. (pseudo-code):

bool executeSomeAction(someValue) {
    switch (someValue) {
    case shouldDoAction1:
        action1();
        break;

    case shouldDoAction2:
        action2();
        break;

    default:
        return false;
    }
    return true;
}

Is it bad practise to write it like this:

bool executeSomeAction(someValue) {
    switch (someValue) {
    case shouldDoAction1:
        action1();
        return true;

    case shouldDoAction2:
        action2();
        return true;

    default:
        return false;
    }
}

Edit: I don't see how this is a duplicate of the linked question, I'm asking about the best practise of writing a function with a single switch statement, I'm not at all asking about using a single or multiple return statements.

Kevin
  • 844
  • 1
  • 7
  • 14
  • 1
    If you need a switch, this could be a sign that a code needs refactoring. When it comes to these two options, I think that first one is better. – Mac70 Sep 28 '16 at 08:05
  • 3
    Um, a switch is a perfectly good structure when used properly. It is very useful if you want to explicitly list out all possibilities, but also have groups of results with the same answers. A switch doesn't mean it needs refactoring. – Nelson Sep 28 '16 at 08:44
  • 2
    @Nelson, my absolute pet hate in programming is sentences of the form "x is a perfectly good feature when used properly". I see folk apply it to singletons, `goto`, inheritance, mutability, `switch` etc etc etc. The problem is, in 99.999% of the time, it isn't used properly. So "don't use feature x" is actually excellent advice. It can then be safely ignored by the tiny group of folk who understand about edge cases where it is needed and only use it in those cases. And from experience those who says "x is a perfectly good feature when used properly" don't know when to use it properly. – David Arno Sep 28 '16 at 09:03
  • 3
    Whether one should use a switch or not is a whole other debate. Lets keep it related. – Kevin Sep 28 '16 at 09:16
  • Possible duplicate of [Where did the notion of "one return only" come from?](http://programmers.stackexchange.com/questions/118703/where-did-the-notion-of-one-return-only-come-from) – gnat Sep 28 '16 at 10:05

4 Answers4

2

It's definitely not bad practice. I use the direct return pattern quite a lot for factory/strategy methods, where different classes implement the same interface and a specific implementation is retrieved based on some strategy.

Not using direct return, having to use the break statement and perhaps having a single point of return from a function may leave the developer reading your code wondering, whether a variable is or is not altered at any further point of the function. Should you return immediately the conclusion is very simple: the function ends.

If you are at a place where you can safely return from a function, do so.

I would choose the option 2 over 1, or even better, change the action1 and action2 functions to return boolean values themselves and do direct return action1() or return action2() respectively.

bool executeSomeAction(someValue)
{
    switch (someValue)
    {
    case shouldDoAction1:
        return action1();
    case shouldDoAction2:
        return action2();
    default:
        return false;
    }
}
Andy
  • 10,238
  • 4
  • 25
  • 50
  • I don't think I should directly return the results of action1 and action2 in my case, but that's certainly worth considering in other cases, I hadn't even thought about that. – Kevin Sep 28 '16 at 08:52
  • I would assume your functions currently return nothing? Or are you just ignoring what they return because they're not relevant to the operation? – Nelson Sep 28 '16 at 09:23
  • @Nelson I'm working on a phonegap plugin, the functions call a bridge to a native API, so it's all a bit more complicated than that – Kevin Sep 28 '16 at 09:47
  • As a counterpoint, I would used option 1 because it provides a single return which means a single place to breakpoint before the function exists when using a debugger. – MikeSchinkel Oct 24 '22 at 20:59
  • @MikeSchinkel, for quite a long time, having a single return from a function/method and thus having to work around this "limitation" is a pattern people have been abandoning, because it introduces cluttered code and the requirement to store or remember the "return" variable value, which worsens readability. In most cases, having a single return is much worse than returning directly. Your debugger argument: you can just as easily place the break point at the place which calls the method with multiple returns to get the same result. I am sorry, your advice is bad in most circumstances. – Andy Oct 26 '22 at 06:22
  • @Andy You make a false dilemma assumption in the general case that the only alternative to “cluttered” code is early return. You are in fact correct for those languages that made the regrettable choice to follow dogma and omit support for goto — Java and ECMAScript to name a few — but for other languages you can have single return AND clean code. Actually, it is code the refactors more cleanly too. But rather than repeat the explanation I’ll just link this essay I wrote on the topic: github.com/mikeschinkel/goto-considered-beneficial – MikeSchinkel Oct 27 '22 at 00:08
  • @MikeSchinkel, I had a look at your essay, I had a look at the golang snippets on which you base your arguments for using goto, I am still not convinced. The snippets you are linking are absolutely awful code and incredibly difficult to read and would never pass code review in a decent development team. Having met some platform developers, just because something is used does not mean it is coded well. In fact a lot of widely C-libs are utter crap when it comes to quality, and the authors know it. You should not use them to advocate your view. Sorry, goto still sucks. – Andy Oct 27 '22 at 06:19
  • @Andy your are, of course, entitled to your own opinion. For that is all that it is. – MikeSchinkel Oct 28 '22 at 10:19
  • @Andy — BTW, I assume you were not aware that you committed a _“No True Scotsman”_ fallacy when you claim my example _”would never pass code review in a decent development team?”_ I take it you would also call Donald Knuth a _“crap coder?”_ https://www.youtube.com/watch?v=XWR5Y3Wf8Fo&t=2610s – MikeSchinkel Oct 28 '22 at 10:24
  • @MikeSchinkel, perhaps sad for you to hear, but yes Mike, I would call Mr. Kruth a crap coder, when it comes to today's standards. Everything evolves, programming languages, methodologies. The top programmes of current days are simply better than top programmers 30 years ago, because they have the benefit of not repeating mistakes previous people had to go through. Goto is one of those. Easily produces memory mismanagement, give you false confidence longer functions are fine, because you skip most of them, makes code difficult to test due to length. It has no place in modern proper dev cycle. – Andy Nov 01 '22 at 12:04
  • @Andy — Yes, standards evolve, but Knuth _still_ uses `goto` as he states in that recent video. None of your arguments against `goto` are accurate for responsible use, you are merely parroting arguments against using `goto` irresponsibly, which ironically modern languages no longer allow; jumping across functions, for example. And further irony, one of your arguments is because of longer functions! Who would think long function are not a mistake in modern programming? Better to shorten your functions then `goto` is not an issue. Sorry but your protestations are based on dogma, not analysis. – MikeSchinkel Nov 03 '22 at 14:37
  • @MikeSchinkel, my protestations come from years of experience building large systems for billion dollar companies, both as a programmer and enterprise architect. Nobody on the teams I have worked at and technically guided ever even considered goto, because there is always a better, more testable way to do the same - by decomposing the code into smaller structures. If your methods are not long, there simply is not any reason to use goto. If your methods are long, you should think about refactoring your code. Either way, I respect your opinion, but would not want to have your ways on my team. – Andy Nov 04 '22 at 06:44
  • @Andy — First I appreciate your comment about respecting my opinion so I will do my best to be equally respectful. Similarly I also have years of experience of programming — 3+ decades actually — on system large as well as small. My advocacy for `goto` is based on that experience, and I only started using `goto` on less than 3 years ago. Prior to that I used tortured approaches with loops and breaks for 5 years to achieve the same end goal, until the best C programmer I know said _"Why not use GOTO instead?"_ What I have found is `goto` solves many issues I experienced earlier in my career. – MikeSchinkel Nov 05 '22 at 19:16
  • @Andy — All that time I found the a `break`/`goto` to a cleanup before a `return` Hs numerous benefits over other approaches — including early `return` — as I documented in my linked repo. What I have not done yet, but plan to do over time, is provide detailed examples to illustrate those benefits. The **only** downside I have found to using `goto` is the knee-jerk predjudice people have against it for reasons that mostly no longer apply. – MikeSchinkel Nov 05 '22 at 19:18
  • @Andy — As for your latest arguments against, I hope you will take time and actually write some real-world code with `goto` — if you program in a language that supports it — before you immediately challenge it again. Respectfully, your arguments against sound very much like the reason for the protest _"I do not like Green Eggs and Ham."_ You claim nobody on the teams have ever considered `goto`. How can you know it is bad when you and your teams have never even considered using it? I assert you must actually use it for a while — as I have prescribed — to find a valid argue against it. – MikeSchinkel Nov 05 '22 at 19:21
  • @MikeSchinkel, I understand why a C programmer supported goto, since C is extremely low level language and it makes working with procedural language easier. But does that mean you should use it in higher level language? You mention cleanup, if your language supports it, why not just instead use RAII-like implementation? Certain languages also support simplifications for cross-cutting concerns (such as logging), once again removing the need for goto. AFAIK higher level languages were invented to also reduce the risk of programmer error - see Rust - and we should prefer to use their features. – Andy Nov 07 '22 at 10:53
0

I don't believe it is bad practice to write such code. On the contrary, I believe that it is very good practice. If the execution reaches a point where the function has nothing more to do, returning immediately is the best and most obvious thing to do. If I read return, I know immediately what is going on. If I read break, I first have to check where the control flow jumps next.

That said, I'd be consistent. Either each and every case (including the default) should contain a return or none. The mixing as in your first snippet is even less readable, I think. Since a switch is already hard to follow, I try to keep it as simple (least surprises for the reader) as possible.

5gon12eder
  • 6,956
  • 2
  • 23
  • 29
0

I would definitely go with the second option, based on the KISS principle. There is absolutely no gain in breaking, and then returning in the next instruction.

The same principle applies in the following snippet which I see more often than I would like to when doing code review:

if (somecondition)
     return true;
else
     return false;

If there is no logging or anything else involved, then just replace the above snippet with:

return somecondition;
Vladimir Stokic
  • 2,943
  • 14
  • 25
  • `if (somecondition == true)` … – 5gon12eder Sep 28 '16 at 09:12
  • 1
    The gain in breaking would be that if the function ever changes in a way that makes it impossible to return directly from within the case statements, there is less refactoring, making it less likely to introduce new bugs in the future. – Kevin Sep 28 '16 at 09:17
  • Then you are breaking YAGNI. Until a need arises, do not overengineer the code, unless you are absolutely, 100% certain that the code will change in the proximate future. – Vladimir Stokic Sep 28 '16 at 09:50
  • 1
    @5gon12eder I can understand adding == false, because it might make the code more readable, e.g. if (!somecondition) as opposed to if (somecondition == false). However, adding == true to the condition makes the code more cluttered to me, so personally I avoid doing that. – Vladimir Stokic Sep 28 '16 at 09:59
  • 1
    @VladimirStokic I wish there was a principle for not trying to shoehorn every damn principle in every project. Adhering to things like DRY, KISS, YAGNI, etc can have great advantages, but they aren't 100% universal and should definitely not be the baseline for all code in all projects. – Kevin Sep 28 '16 at 10:32
  • @Kevin I was just trying to use a known principles (KISS, YAGNI) to demonstrate what some people before me have already explained. If I saw a break in a switch-case statement, I would expect to see something happen afterwards, thus introducing unnecesarry confusion and complexity in the code. Returning directly from from the case statement if there is nothing afterwards is just common sense, that has been verbalized in KISS and YAGNI principles. It is not shoehorning or anything like that. – Vladimir Stokic Sep 28 '16 at 11:29
  • @VladimirStokic Actually, my first comment was meant to be a joke, but now I'm not so sure anymore whether it was understood as such. I totally agree with your analysis. Except that I'm not happy about `expression == false` either. – 5gon12eder Sep 29 '16 at 17:47
  • @VladimirStokic — as a counterpoint, when people use multiple `return` statements _**when they could easily have used one**_ then when I have to debug through the function and need to stop before the function exists, I have to breakpoint multiple places, and if I miss breakpointing a return I can end up chasing my tail for much longer than idea while debugging. YAGNI can easily turn into dogma as I would argue it has here. – MikeSchinkel Oct 24 '22 at 21:03
-5

Either way is okay. Personally, I usually wouldn't return() in the middle of a function's code. Rather, I usually have a block of code at the end of each function labelled "end_of_job:" that does any necessary housekeeping, fprintf()'s any debugging messages (under a guard), etc. Even if your function doesn't currently need any of this, you may want or need it later. And maybe your return(value); will change sometime down the road -- you don't want to miss editing any in-the-middle return()'s. Similarly, other people maintaining your code won't have opportunities to overlook any such "hidden" return()'s. Instead, anyplace where return() is needed, I just have "goto end_of_job;" instead. Here's a mocked-up example...

/* ---
 * end-of-job
 * ------------- */
end_of_job:
  /* --- eoj housekeeping --- */
  if ( temp_buffer != NULL ) free(temp_buffer);
  /* --- debugging --- */
  if ( msglevel>=99 && msgfp!=NULL ) {
    fprintf(msgfp,"func_name> this=%d, that=%f, etc\n",
    this,that,etc); fflush(msgfp); }
  /* --- back to caller --- */
  return(whatever);
} /* --- end-of-function func_name --- */
John Forkosh
  • 821
  • 5
  • 11
  • 4
    This all sounds horrible... – Kevin Sep 28 '16 at 10:28
  • 1
    Please, John, don't do this anywhere in production. Learn a better way. – Andy Sep 28 '16 at 11:37
  • Usually msglevel=0, so nothing's displayed. Also, there's typically a corresponding fprintf at the entry point of each function, displaying all inout arguments, to the extent feasible. That way, when there's a bug, you bump msglevel, and **immediately** see the entire program flow, along with all function inputs/outputs. Usually (though not 100% always) way better than running the debugger and setting breakpoints. And most languages don't have multiple entry points for functions (Fortran being an exception). Multiple return points are typically a similarly less-than-ideal idea. – John Forkosh Sep 29 '16 at 07:18
  • Every time I feel tempted to add such “headings comments” indicating what the following block of code does next, I know that I should refactor. – 5gon12eder Sep 29 '16 at 17:49