Quick. Without testing or looking things up, tell me what is the outcome of running the following code:
let x = 5 switch x { case 5: print("5") fallthrough case 6: print("6") default: print("anything else") }
A lot of people get it wrong, including some coders I was helping out last evening. Fallthrough does have a compelling use case. But it doesn’t do what many people think it will, which can be summed up as “execute this case and then continue pattern matching”. When you’re looking for the conjunction of multiple tests without complex if-then-else statements, don’t use fallthrough. Either conjoin the cases with an associated where
clause or use tuples.
My coder was attempting to test two variables, an integer count and a string as a proof of concept. His goal was to print “High count” for counts over 3. He also wanted to print either long or short name, depending on whether the string was over 5 characters, with appropriate capitalization. These are his four possible outcomes: “High count, long name”, “High count, short name”, “Long name”, and “Short name”.
A tuple approach might look like this:
func checker(_ count: Int, _ name: String) { let highCount = count > 3 let longName = name.characters.count >= 5 switch (highCount, longName) { case (true, true): print("High count, long name") case (true, _): print("High count, short name") case (_, false): print("Long name") case _: print("Short name") } }
And a where
clause approach like this:
func checker(_ count: Int, _ name: String) { let charCount = name.characters.count switch count { case 4 ... .max where charCount >= 5: print("High count, long name") case 4 ... .max: print("High count, short name") case _ where charCount >= 5: print("Long name") case _: print("Short name") } }
Although these demonstrate how switch statements can handle multiple conditions without fallthrough, I don’t particularly like their code readability. I think the cases for this particular example would be better served with more explicit patterns:
case (true, true): print("High count, long name") case (true, false): print("High count, short name") case (false, true): print("Long name") case (false, false): print("Short name")
When you go to the trouble of creating complex logic in switch statements, do everything you can to make the code self-document its conditions. This better supports inspection and enables the compiler to find incomplete logic coverage on your behalf.
2 Comments
This is really interesting as an old C/C++ programmer. It’s drilled into you that if you don’t add a break clause then the next case will execute. I’d assumed that the swifty way of making break the default that anyone adding a falltthrough would invert that logic, rather than be stuck with a third or fourth option regarding their inputs.
The example above executed exactly as I expected it to, so maybe Swift has been a bit too clever in assuming old heads on new shoulders. I guess sometimes when you fix one problem you create two more.
You just need to be aware of what fallthrough is doing. Like usually 😉