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.)
Swift compiler crashes on switch statement, need workaround
Credit to the solution really goes to everyone who commented above.
The problem seems to be that the Swift compiler wants to know exactly how large the enum is in memory. Generics make that impossible, thus the compiler does not behave correctly.
The solution I went with was taken from Rob Rix's library, which is to box the generic in another type. Note that it must be a class, since a reference has a known size but a struct with the generic does not.
The @autoclosure solution seemed interesting also, but it does not work with the latest version of Xcode. This was by design; as I understand, the developers don't want closures to run each time the enum is evaluated.
Thanks everyone!
Expected declaration on switch declaration
You can't write switch rank {
inside the struct directly , it should be inside a function init
or any other custom one
public struct beltRank {
var rank = 0
var belt = ""
init(rank:Int) {
// write switch here
}
}
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 switch default + fallthrough: missing return in a function expected to return 'String'
It's not clear whether you have figured this out or not. The problem is not that you haven't got all bases covered in your switch statement. It is that not all code paths return a value. In the ApiError case, if the test fails, there is no return executed. If you put a return
after this test, the code will compile.
Related Topics
Swift Combine How Set<Anycancellable> Works
Destructuring Tuple of Tuple in Closure
How to Put View on Top of All Other Views in Swiftui
Viewwilllayoutsubviews in Swift
Scrolltoitem at Indexpath at .Top Hides Cell Under Header When Sectionheaderspintovisiblebounds
Swift Navigation Bar Item Not Calling Action
Implement Protocol with Different Associated Type
Pfobject Unable to Be Cast to Custom Subclass
"The Requested Snapshot Version Is Too Old." Error in Firestore
Swift Increment Int! Not Working
Difference Between Object(Forkey:) and Value(Forkey:) in Userdefaults
Swiftui CPU High Usage on Real-Time Foreach View Updating (Macos)
Set an Horizontal Scroll to My Barchart in Swift
Pass Type to Generic Function and Compare
Reachability Change Notification Should Be Called Only Once