Should switch statements always contain a default clause?
Asked Answered
E

22

363

In one of my first code reviews (a while back), I was told that it's good practice to include a default clause in all switch statements. I recently remembered this advice but can't remember what the justification was. It sounds fairly odd to me now.

  1. Is there a sensible reason for always including a default statement?

  2. Is this language dependent? I don't remember what language I was using at the time - maybe this applies to some languages and not to others?

Eberly answered 10/1, 2011 at 17:13 Comment(1)
It's going to be language-dependent to a large degreeFawn
T
343

Switch cases should almost always have a default case.

Reasons to use a default

1.To 'catch' an unexpected value

switch(type)
{
    case 1:
        //something
    case 2:
        //something else
    default:
        // unknown type! based on the language,
        // there should probably be some error-handling
        // here, maybe an exception
}

2. To handle 'default' actions, where the cases are for special behavior.

You see this a LOT in menu-driven programs and bash shell scripts. You might also see this when a variable is declared outside the switch-case but not initialized, and each case initializes it to something different. Here the default needs to initialize it too so that down the line code that accesses the variable doesn't raise an error.

3. To show someone reading your code that you've covered that case.

variable = (variable == "value") ? 1 : 2;
switch(variable)
{
    case 1:
        // something
    case 2:
        // something else
    default:
        // will NOT execute because of the line preceding the switch.
}

This was an over-simplified example, but the point is that someone reading the code shouldn't wonder why variable cannot be something other than 1 or 2.


The only case I can think of to NOT use default is when the switch is checking something where its rather obvious every other alternative can be happily ignored

switch(keystroke)
{
    case 'w':
        // move up
    case 'a':
        // move left
    case 's':
        // move down
    case 'd':
        // move right
    // no default really required here
}
Toney answered 9/3, 2011 at 4:6 Comment(8)
Your example regarding keystrokes kind of contradicts the rest of what you just said. :| I think a bit more explanation would make sense such as: you don't care about a default in this case because if another key is pressed you don't care, but if a variable is passed in is different than expected, we do care.Flavorsome
At the same time if you are checking a GET parameter, you might not want an exception thrown every time a user changes the get url to something that is not valid :[Flavorsome
Switch condition is an Enum with no default value. Should we still have a default case in the switch statement?Thornburg
@Flavorsome W-A-S-D is common for computer game character movements.You wouldn't need any default action as other keys could be assigned to other interactions. In this kind of switch, its good to call movement functions.Orban
Some compilers warn when switching on an enum if a case is missed, and adding a default case will suppress that warning. Given that this is in my experience a very common source of error (forgetting to update a switch somewhere after an enum value is added), this seems like a very good reason to omit a default case.Disannul
@Thornburg If your condition is an enum, you can still have unexpected values. For example, if you add a new value to the enum, and forget to update a switch. Also, in some languages you can assign integer values to enums. In C++ enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2; makes a valid MyEnum instance that is not equal to FOO or BAR. If either of these issues would cause an error in your project, a default case will help you find the error very quickly, often with no debugger necessary!Buttock
I realize @Disannul mentions the same thing as my first example as a reason to omit a default case, so I wanted to address that here. rdb's solution will catch the error in example #1 faster than my solution, but they would both work. However, example #2 causes an error that my solution catches, and rdb's does not. Finally, I'd like to say that example #2 is less common (unless you're incrementing enums) than example #1, and I'm sure some languages don't even allow example #2 to occur, so the decision should be based on the project, as well as the language being used.Buttock
"To show someone reading your code that you've covered that case." - Is it also applicable to if-else? That is, should we use if-else if-else instead of simply if-else if?Kp
B
85

No.

What if there is no default action, context matters. What if you only care to act on a few values?

Take the example of reading keypresses for a game

switch(a)
{
   case 'w':
     // Move Up
     break;
   case 's':
     // Move Down
     break;
   case 'a':
     // Move Left
     break;
   case 'd':
     // Move Right
     break;
}

Adding:

default: // Do nothing

Is just a waste of time and increases the complexity of the code for no reason.

Basrhin answered 11/3, 2011 at 4:40 Comment(11)
Good example. But in fact, adding a default clause with a simple // do nothing comment makes it clear that it is 'ok' if not all cases are covered, as opposed to other switch statements where this would be 'not ok'.Forethought
No. Coding is not only about handling some cases. Also it serves as a document. By writing default: and commenting like // Do nothing or something else, readability of code becomes better.Lamentable
Disagree with the other comments. Adding default // Do nothing adds very little unless you don't know how code works. I can look at a switch statement without a default and know that the default is to do nothing. Adding clutter does not make this more obvious.Windblown
Exactly. It is in the same concept of handing al post params. You handle the ones you care of not the others.Savour
In the spirit of the answer, you can remove the last break.Mainspring
The final break is there for visual consistencyBasrhin
Instead of adding an empty default with a horrible comment to clarify, you could encapsulate the whole switch in a method named something like actOnMovementKeystrokes. This also makes it clear that is ok if not all cases are covered. Bonus: you get a better designed class...Noncombatant
@RobertNoack - sorry, I disagree. Adding a default // do nothing lets other programmers know you did not accidentally forget to include a default case. However, I would be ok with a comment explaining there is no default needed.Stephenstephenie
@jybrd If you don't trust that the previous developer left out the default intentionally, why would you trust that they were correct to put it in? A default could be accidently empty just as easy as missing entirely. A comment is not necessary for something so trivial. This is code clutter. Write full coverage unit tests to show your intentions instead. (Just my opinion)Windblown
@Gil Vegliach And the best comment award goes to you.Parody
@GilVegliach I'm sad this is up voted and praised, as it's very bad practice. Please don't EVER do this. You leave the last break in there because otherwise you're introducing very easy to create bugs. When the next programmer comes along and adds another key press to check at the end of the list, it creates not just a bug but a SILENT bug that is hard to catch. Maybe I misunderstood and your point was it's bad to both leave out the break and the default, but I personally don't remember any bugs from default statements being forgotten and agree it's needless code clutter that's obvious.Uproot
T
82

NOT having the default case can actually be beneficial in some situations.

If your switch cases are enums values, by not having a default case, you can get a compiler warning if you are missing any cases. That way, if new enum values are added in the future and you forget to add cases for these values in the switch, you can find out about the problem at compile time. You should still make sure the code takes appropriate action for unhandled values, in case an invalid value was cast to the enum type. So this may work best for simple cases where you can return within the enum case rather than break.

enum SomeEnum
{
    ENUM_1,
    ENUM_2,
    // More ENUM values may be added in future
};

int foo(SomeEnum value)
{
    switch (value)
    {
    case ENUM_1:
        return 1;
    case ENUM_2:
        return 2;
    }
    // handle invalid values here
    return 0;
 }
Thallus answered 19/6, 2015 at 18:10 Comment(5)
This is an important observation! It applies even more in Java because it doesn't allow you to cast ints to enums.Acentric
In your example you could throw an exception instead of returning after the switch-statement. In that way you could have both static checking by the compiler and get a clear error if something unexpected happens.Acentric
I agree. In Swift per example, switch must be exhaustive otherwise you get a compiler error! Providing a default just silences this useful error. On the other hand, providing a default if all cases are handled actually throws a compiler warning (unreachable statement).Upshaw
Just fixed a bug, that would have caused a warning from the Compiler if there was no default: so generally a Switch over enum Variables should have no default.Graffito
I have been looking into Rust lately and it has the same sort of behavior as Swift mentioned here. I was thinking: this is so cool, C++ never checked that I have it all handled. But it actually checks it. In GCC the -Wswitch is on if -Wall is used (which is pretty much every time). The reason it never helped me is that we have a mandatory coding rule in my company that says you have to add default in every switch, and everybody keeps following it blindly.Chak
E
47

I would always use a default clause, no matter what language you are working in.

Things can and do go wrong. Values will not be what you expect, and so on.

Not wanting to include a default clause implies you are confident that you know the set of possible values. If you believe you know the set of possible values then, if the value is outside this set of possible values, you'd want to be informed of it - it's certainly an error.

That's the reason why you should always use a default clause and throw an error, for example in Java:

switch (myVar) {
   case 1: ......; break;
   case 2: ......; break;
   default: throw new RuntimeException("unreachable");
}

There's no reason to include more information than just the "unreachable" string; if it actually happens, you're going to need to look at the source and the values of the variables etc anyway, and the exception stacktrace will include that line number, so no need to waste your time writing more text into the exception message.

Evania answered 10/1, 2011 at 17:21 Comment(9)
I would prefer throw new RuntimeException("myVar invalid " + myVar); since that might give you enough info to figure out and fix the bug without having to use a debugger and inspect variables, which may be difficult if this is a rarely occurring problem that is tough to reproduce.Marzipan
What if it's not an error condition for the value to not match one of the cases?Gromme
If it's not an error condition, then there'd be no need for a default:. But more often that not, one leaves out a default because one thinks myVar can never have any value other than those listed; however I can't count the number of times I've been surprised in real life when the variable has had a value other than the values it "could possibly" have. In those cases, I've been thankful for the exception making me see that right away, rather than causing some other error later (more difficult to debug) or wrong answer (might get overlooked in testing).Evania
I agree with Adrian. Fail hard and fail early.Luettaluevano
I prefer throwing an AssertionError instead of RuntimeException, as this situation is very similar to an assertion stating that myVar is one of the handled values. Also the chance that someone catches and swallows the error is smaller.Poock
Throwing an AssertionError is a good idea, you're right! I suppose you are right about the issue of people potentially swallowing all Exceptions, but I try and not write code with such people ;-)Evania
Throw an error that a human might understand as it's likely default will be hit with something you (the developer) was not expecting.Interphase
I agree with this approach. Just wondering what would happen if you had a default handler on an enum, where there is a case for each value? Would the compiler just remove it? Actually, thinking about it, most languagues allow enums to hold arbitrary values, something you'd definitely want to know about.Lusatia
downvoting as modern languages (such as Typescript or Rust) add this exhaustivity check at compile time (if you do not set a "default" but do not match all possible values neither), and this is really useful for the Developer Experience to be guided by the compiler to see where our code modifications have impact in the codebase, and to avoid shipping silly bugs in production (e.g. when you add some enum variant but forgot to handle all use cases for this new variant)Sherleysherline
G
22

Should a "switch" statement always include a default clause? No. It should usually include a default.

Including a default clause only makes sense if there's something for it to do, such as assert an error condition or provide a default behavior. Including one "just because" is cargo-cult programming and provides no value. It's the "switch" equivalent of saying that all "if" statements should include an "else".

Here's a trivial example of where it makes no sense:

void PrintSign(int i)
{
    switch (Math.Sign(i))
    {
    case 1:
        Console.Write("positive ");
        break;
    case -1:
        Console.Write("negative ");
        break;
    default: // useless
    }
    Console.Write("integer");
}

This is the equivalent of:

void PrintSign(int i)
{
    int sgn = Math.Sign(i);
    if (sgn == 1)
        Console.Write("positive ");
    else if (sgn == -1)
        Console.Write("negative ");
    else // also useless
    {
    }
    Console.Write("integer");
}
Gromme answered 10/1, 2011 at 17:18 Comment(5)
I disagree. IMHO, the only time a default should not exist is if there is no way, for now and ever, that the set of inputs can change, and you've got every possible value covered. The simplest case I can think of is a boolean value from a database, where the only answers (until SQL changes!) are true, false, and NULL. In any other case, having a default with an assertion or exception of "Shouldn't Happen!" makes good sense. If your code changes, and you have one or more new values, then you can ensure you code for them, and your tests will blow up if you don't.Auramine
@Harper: Your example falls under the category of "assert an error condition".Gromme
I'd assert that my example is the norm, and that the small number of cases where one can be 100% sure every possible case is covered and a default isn't needed are the exception. Your answer is worded in a way that makes it sound (at least to me) as if the do-nothing default will happen more often than not.Auramine
@Harper: OK, I changed the wording to indicate that the do-nothing situation is less common.Gromme
I think a default: in those cases codifies your assumptions. For example, is it OK when sgn==0 to print integer (neither positive nor negative), or is that an error? For me reading that code, it's difficult to say. I'd assume you'd want to write zero in that case not integer, and that the programmer made the assumption that sgn can only be -1 or +1. If that were the case, having the default: would allow the programmer to catch the assumption error early and change the code.Evania
F
16

In my company, we write software for the Avionics and Defense market, and we always include a default statement, because ALL cases in a switch statement must be explicitly handled (even if it is just a comment saying 'Do nothing'). We cannot afford the software just to misbehave or simply crash on unexpected (or even what we think impossible) values.

It can be discussed that a default case is not always necessary, but by always requiring it, it is easily checked by our code analyzers.

Featherston answered 10/3, 2011 at 14:48 Comment(4)
I have the same requirement where I work; we write code for embedded µControllers that have to pass strict safety checks and are often subjected to EMI. It would be irresponsible to assume that an enumerated variable will never have a value that is not in the enumeration list.Donn
See the answer of Harlan Kassler for an example why this is a stupid policy which actually hides the errors. Of course his answer assumes a decent compiler, which is not always a given thing.Duala
@Duala This is simply a requirement from the avionics and defense world. Don't call that stupid. It is true that in the mean time compilers have evolved a lot in this area since this question was first answered, but again in that case you need to prove that your compiler is actually working perfectly at detecting those cases. Keep in mind that this is life-critical software.Featherston
I used to work in avionics (and in defense too). It is full of stupid requirements that do not contribute anything to actual safety. Misra guide in particular was shown (lazy to search for the link) to be at best useless to decrease the bug frequency and severity. To prevent misread: I am not saying that all those requirements are stupid. Most of them aren't. But some (and non-negligible proportion) are.Duala
J
9

As far as i see it the answer is 'default' is optional, saying a switch must always contain a default is like saying every 'if-elseif' must contain a 'else'. If there is a logic to be done by default, then the 'default' statement should be there, but otherwise the code could continue executing without doing anything.

Jaquith answered 28/9, 2012 at 19:7 Comment(0)
T
8

I disagree with the most voted answer of Vanwaril above.

Any code adds complexity. Also tests and documentation must be done for it. So it is always good if you can program using less code. My opinion is that I use a default clause for non-exhaustive switch statements while I use no default clause for exhaustive switch statements. To be sure that I did that right I use a static code analysis tool. So let's go into the details:

  1. Nonexhaustive switch statements: Those should always have a default value. As the name suggests those are statements which do not cover all possible values. This also might not be possible, e.g. a switch statement on an integer value or on a String. Here I would like to use the example of Vanwaril (It should be mentioned that I think he used this example to make a wrong suggestion. I use it here to state the opposite --> Use a default statement):

    switch(keystroke)
    {
      case 'w':
        // move up
      case 'a':
        // move left
      case 's':
        // move down
      case 'd':
        // move right
      default:          
        // cover all other values of the non-exhaustive switch statement
    }
    

    The player could press any other key. Then we could not do anything (this can be shown in the code just by adding a comment to the default case) or it should for example print something on the screen. This case is relevant as it may happen.

  2. Exhaustive switch statements: Those switch statements cover all possible values, e.g. a switch statement on an enumeration of grade system types. When developing code the first time it is easy to cover all values. However, as we are humans there is a small chance to forget some. Additionally if you add an enum value later such that all switch statements have to be adapted to make them exhaustive again opens the path to error hell. The simple solution is a static code analysis tool. The tool should check all switch statements and check if they are exhaustive or if they have a default value. Here an example for an exhaustive switch statement. First we need an enum:

    public enum GradeSystemType {System1To6, SystemAToD, System0To100}
    

    Then we need a variable of this enum like GradeSystemType type = .... An exhaustive switch statement would then look like this:

    switch(type)
    {
      case GradeSystemType.System1To6:
        // do something
      case GradeSystemType.SystemAToD:
        // do something
      case GradeSystemType.System0To100:
        // do something
    }
    

    So if we extend the GradeSystemType by for example System1To3 the static code analysis tool should detect that there is no default clause and the switch statement is not exhaustive so we are save.

Just one additional thing. If we always use a default clause it might happen that the static code analysis tool is not capable of detecting exhaustive or non-exhaustive switch statements as it always detects the default clause. This is super bad as we will not be informed if we extend the enum by another value and forget to add it to one switch statement.

Tsosie answered 19/6, 2020 at 9:34 Comment(0)
M
6

I would say it depends on the language, but in C if you're switching on a enum type and you handle every possible value, you're probably better off NOT including a default case. That way, if you add an additional enum tag later and forget to add it to the switch, a competent compiler will give you a warning about the missing case.

Marzipan answered 10/1, 2011 at 17:30 Comment(2)
"Every possible value" is unlikely to be handled with enums. You can cast an integer to an enum even if that particular value is not explicitly defined. And which compiler warns about the missing case?Urease
@TrueWill: Yes, you can use explicit casts to write obfuscated code that is impossible to understand; to write understandable code, you should avoid that. gcc -Wall (sort of the lowest common denominator of competent compilers) gives warnings about enums unhandled in switch statements.Marzipan
W
6

Having a default clause when it's not really needed is Defensive programming This usually leads to code that is overly complex because of too much error handling code. This error handling and detection code harms the readability of the code, makes maintenance harder, and eventually leads to more bugs than it solves.

So I believe that if the default shouldn't be reached - you don't have to add it.

Note that "shouldn't be reached" means that if it reached it's a bug in the software - you do need to test values that may contain unwanted values because of user input, etc.

Warford answered 9/3, 2011 at 9:57 Comment(2)
Another common reason to get unexpected cases: Other parts of the code are modified, perhaps years later.Wowser
Indeed, this is very dangourous behaviour. At least, I would add an assert() clause. Then it is easily detected whenever there is a bug.Featherston
V
4

If you know that the switch statement will only ever have a strict defined set of labels or values, just do this to cover the bases, that way you will always get valid outcome.. Just put the default over the label that would programmatically/logically be the best handler for other values.

switch(ResponseValue)
{
    default:
    case No:
        return false;
    case Yes;
        return true;
}
Valuable answered 2/10, 2012 at 1:36 Comment(2)
Is the colon after default still required here? Or does doing this allow some special syntax that allowed you to omit it?Lotte
The colon is required, that was a typo, thank you for bringing it to my attention.Valuable
T
4

It is better to have both a static check and a dynamic check than only the dynamic check. That's a reason against defaults in enum switches.

Leaving out the default case enables the compiler to optionally warn or fail when it sees an unhandled case. If each case is either a return or break – wrap it in a lambda if necessary, the effective default is conveniently the section after the switch. Which the compiler will insist is reachable, and as such must be handled (because the switch argument may physically be an invalid value, even if that would be logically invalid).

const char *name = [trafficLight]() {
    switch (trafficLight) {
        case TrafficLight::Green: return "Green";
        case TrafficLight::Yellow: return "Yellow";
        case TrafficLight::Red: break;
    }
    return "Red";
}();

Some guidelines mandate a default in all switches, such as MISRA C:

The requirement for a final default clause is defensive programming. This clause shall either take appropriate action or contain a suitable comment as to why no action is taken.

The typical need for a dynamic check is input handling, in a wide sense. If a value comes from outside the program's control, it can't be trusted.

This is also where Misra takes the standpoint of extreme defensive programming, whereby as long as an invalid value is physically representable, it must be checked for, no matter if the program is provably correct. Which makes sense if the software needs to be as reliable as possible in the presence of hardware errors. But as Ophir Yoktan said, most software are better off not "handling" bugs. The latter practice is sometimes called offensive programming.

Tripodic answered 5/9, 2016 at 21:12 Comment(0)
P
3

Atleast it is not mandatory in Java. According to JLS, it says atmost one default case can be present. Which means no default case is acceptable . It at times also depends on the context that you are using the switch statement. For example in Java, the following switch block does not require default case

private static void switch1(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        break;
    case "Tuesday":
        System.out.println("Tuesday");
        break;
    }
}

But in the following method which expects to return a String, default case comes handy to avoid compilation errors

    private static String switch2(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        return name;

    case "Tuesday":
        System.out.println("Tuesday");
        return name;

    default:
        return name;
    }
}

though you can avoid compilation error for the above method without having default case by just having a return statement at the end, but providing default case makes it more readable.

Pursuit answered 14/11, 2013 at 13:44 Comment(0)
C
3

If the switch value (switch(variable)) can't reach the default case, then default case is not at all needed. Even if we keep the default case, it is not at all executed. It is dead code.

Carden answered 8/1, 2014 at 15:4 Comment(0)
C
3

It is an optional coding 'convention'. Depending on the use is whether or not it is needed. I personally believe that if you do not need it it shouldn't be there. Why include something that won't be used or reached by the user?

If the case possibilities are limited (i.e. a Boolean) then the default clause is redundant!

Compact answered 28/5, 2014 at 12:13 Comment(0)
V
2

Depends on how the switch in particular language works, however in most languages when no case is matched, the execution falls through the switch statement without warning. Imagine you expected some set of values and handled them in switch, however you get another value in the input. Nothing happens and you don't know nothing happened. If you caught the case in default, you would know there was something wrong.

Valentia answered 10/1, 2011 at 17:19 Comment(0)
I
2

You should have a default to catch un-expected values coming in.

However, I disagree with the Adrian Smith that your error message for default should be something totally meaningless. There may be an un-handled case you didn't forsee (which is kind of the point) that your user will end up seeing and a message like "unreachable" is entirely pointless and doesn't help anyone in that situation.

Case in point, how many times have you had an utterly meaningless BSOD? Or a fatal exception @ 0x352FBB3C32342?

Interphase answered 11/3, 2011 at 4:21 Comment(1)
eg - remember it's not only the developer that always sees error messages. We live in the real world, people make mistakes.Interphase
N
2

If there is no default case in a switch statement, the behavior can be unpredictable if that case arises at some point of time, which was not predictable at development stage. It is a good practice to include a default case.

switch ( x ){
  case 0 : { - - - -}
  case 1 : { - - - -}
}

/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */

Such a practice can result in a bug like NULL dereference, memory leak as well as other types of serious bugs.

For example we assume that each condition initializes a pointer. But if default case is supposed to arise and if we don’t initialize in this case, then there is every possibility of landing up with a null pointer exception. Hence it is suggested to use a default case statement, even though it may be trivial.

Nocturn answered 29/1, 2016 at 15:52 Comment(0)
T
2

The default case may not necessary in the switch used by enum. when switch contained all value, the default case will never execute. So in this case, it is not necessary.

Trevethick answered 20/10, 2016 at 7:4 Comment(0)
M
0

Should switch statements always contain a default clause ? No switch cases can exist with out default case, in switch case default case will trigger switch value switch(x) in this case x when not match with any other case values.

Manvel answered 1/11, 2017 at 19:58 Comment(0)
R
0

I believe this is quite language specific and for the C++ case a minor point for enum class type. Which appears more safe than traditional C enum. BUT

If you look at the implementation of std::byte its something like:

enum class byte : unsigned char {} ;

Source: https://en.cppreference.com/w/cpp/language/enum

And also consider this:

Otherwise, if T is a enumeration type that is either scoped or unscoped with fixed underlying type, and if the braced-init-list has only one initializer, and if the conversion from the initializer to the underlying type is non-narrowing, and if the initialization is direct-list-initialization, then the enumeration is initialized with the result of converting the initializer to its underlying type.

(since C++17)

Source: https://en.cppreference.com/w/cpp/language/list_initialization

This is an example of enum class representing values that are not defined enumerator. For this reason you cannot place complete trust in enums. Depending on application this might be important.

However, I really like what @Harlan Kassler said in his post and will start using that strategy in some situations myself.

Just an example of unsafe enum class:

enum class Numbers : unsigned
{
    One = 1u,
    Two = 2u
};

int main()
{
    Numbers zero{ 0u };
    return 0;
}
Republican answered 2/7, 2019 at 5:27 Comment(0)
S
0

I think this is old advice now. In java, the compiler can detect missing values if you are using switch expressions, and give an error. Then it instead becomes a bad practice to add a default, because you will not know that a switch statement broke when you added a new enum vaule for example.

Sergu answered 22/9, 2023 at 9:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.