If the cases of my switch on an Int cover every number from Int.min to Int.max, why does it still make me put a default?
For example:
let number = 5
switch number {
case Int.min ... 0:
print("Too small")
case 1...100:
print("Reasonable")
case 101 ... Int.max:
print("Too big")
}
Even though there isn’t a value number could be that is not covered by these three cases, I still get “Switch must be exhaustive” (in a Swift Playground).
I know I could change the final case to default: or case _: to satisfy the compiler, but at the cost of expressiveness. Why doesn’t the compiler recognize that this switch is exhaustive?
Unfortunately, integer operations like '...' and '<' are just plain functions to Swift, so it'd be difficult to do this kind of analysis. Even with special case understanding of integer intervals, I think there are still cases in the full generality of pattern matching for which exhaustiveness matching would be undecidable. We may eventually be able to handle some cases, but there will always be special cases involved in doing so.
Then wouldn't it be better for the compiler error to ask for a default case? That would save some time for newbies, who will most likely spend time trying to be exhaustive before adding default case.
Or the compiler could just do the "right thing" and put in a default case with an assertion. If the existing cases are truly exhaustive - no harm done; otherwise, for newbies, they get an assertion for a missing case.
func foobar(v: (Bool, Bool)) {
switch v {
case (false, false): print("false false")
case (false, true): print("false true")
case (true, false): print("true false")
case (true, true): print("true true")
// default clause is not needed, all possible cases are enumerated
}
}
As mentioned above this is a known limitation. Also, merely putting things in a struct breaks exhaustiveness checks:
struct S: Equatable {
let bool: Bool
}
func foobar(v: S) {
switch v { // 🛑 Switch must be exhaustive
case S(bool: false): print("false")
case S(bool: true): print("true")
}
}
Bool exhaustiveness checks are not perfect either:
func foo(_ v: Bool) {
let myfalse = false
switch v {
case false: print("false");
case true: print("true")
case myfalse: print("myfalse") // ✅ expected warning: "Case will never be executed"
}
switch v {
case myfalse: print("myfalse")
case false: print("false") // 👎 no warning here
case true: print("true")
}
switch v {
case false: print("false")
case myfalse: print("myfalse") // 👎 no warning here
case true: print("true")
}
switch v { // 👎 🛑 unexpected error: Switch must be exhaustive
case myfalse: print("myfalse")
case true: print("true")
}
switch v { // 🛑 unexpected error: Switch must be exhaustive
case false: print("myfalse")
case !false: print("true")
}
}