A Switch Bug in Swift? - "Switch Must Be Exhaustive, Consider Adding a Default Clause."

Switch Statement Must Be Exhaustive - Xcode bug?

If you write it like this, you won't get an error (Xcode 9 beta 2):

import Foundation

enum Search {
case sets(query: String?, creator: String?, imagesOnly: Bool?, autocomplete: Bool?, modifiedSince: TimeInterval?, page: Int?, perPage: Int?)
case classes(query: String, page: Int?, perPage: Int?)
case universal(query: String, page: Int?, perPage: Int?)

public var baseURL: String { return "" }
public var version: Float { return 0 }

public var path: String {
switch self { // Switch must be exhaustive ERROR.
case .sets(_, _, _, _, _, _, _):
return "search/sets"
case .classes(_, _, _):
return "search/classes"
case .universal(_, _, _):
return "search/universal"
}
}
}

If I'm going to be doing much matching, I usually prefer to wrap the associated values in separate structs. This makes for simpler patterns and for less work when changing the nature of the associated values. Thus:

enum Search {

struct Sets {
var query: String?
var creator: String?
var imagesOnly: Bool?
var autocomplete: Bool?
var modifiedSince: TimeInterval?
var page: Int?
var perPage: Int?
}

case sets(Sets)

struct Classes {
var query: String
var page: Int?
var perPage: Int?
}

case classes(Classes)

struct Universal {
var query: String
var page: Int?
var perPage: Int?
}

case universal(Universal)

public var baseURL: String { return "" }
public var version: Float { return 0 }

public var path: String {
switch self { // Switch must be exhaustive ERROR.
case .sets(_):
return "search/sets"
case .classes(_):
return "search/classes"
case .universal(_):
return "search/universal"
}
}
}

Swift 3.0-Switch statement without default statement getting error 'Switch must be exhaustive, consider adding default clause'

As other stated in comments, you should use default because in your cases you're not exposing every possible Double. But if you like more the way you did it in your second example you could do it like so:

let someNumber = 3.5
switch someNumber {
case 2 , 3 , 5 , 7 , 11 , 13 :
print("Prime numbers")
case 4 , 6 , 24 , 12 , 66 :
print("Normal numbers")
case let x:
print("I also have this x = \(x)")
}

Just for the reference, here's how this scenario is most often handled:

let someNumber = 3.5
switch someNumber {
case 2 , 3 , 5 , 7 , 11 , 13 :
print("Prime numbers")
case 4 , 6 , 24 , 12 , 66 :
print("Normal numbers")
default:
print("I have an unexpected case.")
}

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
}

Switch statement must be exhaustive

Xcode checks if the switch statement is exhaustive only if you're switching enums. For every other case, it checks if there is a default statement, and if not, it puts up a warning.

You can either use enums, or squelch the warning if you want to, or, just add the missing default statement doing nothing.

Swift switch statement considered all cases of Int, but compiler still display error

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.)

Should switch statements always contain a default clause?

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
}


Related Topics



Leave a reply



Submit