8

I understand switch statement in Swift must be exhaustive, otherwise we have to provide an default case. I saw the code below online, the switch statement already have covered all cases in Int, but the compiler still display error message that switch must be exhaustive, consider adding a default clause. Is there something I'm missing?

extension Int { enum Kind { case Negative, Zero, Positive } var kind: Kind { switch self { case 0: return .Zero case let x where x > 0: return .Positive case let x where x < 0: return .Negative } } } 
1
  • Note that you needn't use the intermediate x variable in this case, as you can use pattern matching for anything (_) with an additional where clause, e.g. case _ where self > 0: .... Commented Apr 7, 2016 at 15:04

1 Answer 1

21

Update for Swift 3: Swift 3 introduced ClosedRange which makes it possible to define a range like 1...Int.max including the largest possible integer (compare Ranges in Swift 3). So this compiles and works as expected, but still requires a default case to satisfy the compiler:

extension Int { enum Kind { case negative, zero, positive } var kind: Kind { switch self { case 0: return .zero case 1...Int.max: return .positive case Int.min...(-1): return .negative default: fatalError("Oops, this should not happen") } } } 

There are other bug reports where the Swift compiler does not correctly determine the exhaustiveness of switch-statements, such as https://bugs.swift.org/browse/SR-766, where Apple engineer Joe Groff commented:

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.


Old answer: The compiler is not so smart to recognize that you have covered all possible cases. One possible solution is to add a default case with a fatalError():

var kind: Kind { switch self { case 0: return .Zero case let x where x > 0: return .Positive case let x where x < 0: return .Negative default: fatalError("Oops, this should not happen") } } 

Or make case 0: the default case:

var kind: Kind { switch self { case let x where x > 0: return .Positive case let x where x < 0: return .Negative default: return .Zero } } 

(Remark: I initially thought that the following would work correctly without needed a default case:

var kind: Kind { switch self { case 0: return .Zero case 1 ... Int.max: return .Positive case Int.min ... -1: return .Negative } } 

However, this compiles, but aborts at runtime because you cannot create the range 1 ... Int.max. More information around this problem can be found in the article Ranges and Intervals in Swift.)

Sign up to request clarification or add additional context in comments.

7 Comments

That's a really unfortunate limitation of ranges that you can't create one that goes up to the maximum value.
Unfortunately, using case 1 ..< Int.max, Int.max: just returns you to the compiler asking for a default case.
@vacawama: Yes, I had already tried that. And case 0: / case 5 ... Int.max: / case Int.min ... -5: compiles even though the cases are not exhaustive.
Wow. Swift still has some rough edges apparently.
In light of limit case the above, could the internal storage of open ranges [from,through] as from ..< through.successor() (even if specified as from ... through) be seen as a limitation that should be prompted to the Swift team? Present, however, only for the limiting case when through equals the maximum value of the particular type (in which case .successor() overflows).
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.