Exhaustive condition of switch case in Swift
Swift only truly verifies that a switch
block is exhaustive when working with enum
types. Even a switching on Bool
requires a default
block in addition to true
and false
:
var b = true
switch b {
case true: println("true")
case false: println("false")
}
// error: switch must be exhaustive, consider adding a default clause
With an enum
, however, the compiler is happy to only look at the two cases:
enum MyBool {
case True
case False
}
var b = MyBool.True
switch b {
case .True: println("true")
case .False: println("false")
}
If you need to include a default
block for the compiler's sake but don't have anything for it to do, the break
keyword comes in handy:
var b = true
switch b {
case true: println("true")
case false: println("false")
default: break
}
Part of why you see that error because the compiler can't verify that switch is exhaustive without running code. The expression 0...65535
creates a ClosedInterval
struct, and when the switch statement executes it has to ask that struct if the value quantity
is in the interval. There's room for that to change at run time, so the compiler can't check it at compile time. (See the Halting Problem.)
More generally, the compiler can't detect an exhaustive switch for integer values — even if you add specific cases for every integer value (case 0: ... case 1: ... ... case 65535:
), it doesn't know your switch is exhaustive. (Theoretically it could, though: consider filing a feature request about this if it's something you'd like to see.)
As it stands, there are two scenarios where Swift can detect completeness and allow you to omit the default
clause: enums and value binding in tuples. @NateCook's answer covers enums — if you switch on an enum value and have a case
in your switch
for every case
in the enum, you don't need a default
. You also don't need a default
label if you switch on a tuple and bind every possible combination of values, as seen in the Swift book:
switch anotherPoint {
case (let x, 0):
println("on the x-axis with an x value of \(x)")
case (0, let y):
println("on the y-axis with a y value of \(y)")
case let (x, y):
println("somewhere else at (\(x), \(y))")
}
You might generalize this rule as "if the type system knows about the possible values of your type, it can detect switch
completeness", but the fact that there's a level on which the type system doesn't know the range of possible (e.g.) UInt32
values is sort of splitting hairs...