Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
        }
    }
}
like image 716
Thor Avatar asked Apr 07 '16 12:04

Thor


People also ask

Does switch case work with integers?

A switch works with the byte , short , char , and int primitive data types. It also works with enumerated types (discussed in Enum Types), the String class, and a few special classes that wrap certain primitive types: Character , Byte , Short , and Integer (discussed in Numbers and Strings).

What will happen if the switch statement did not match any cases?

If no matching case clause is found, the program looks for the optional default clause, and if found, transfers control to that clause, executing statements following that clause. If no default clause is found, the program continues execution at the statement following the end of switch .

How many cases should a switch statement have?

ANSI C requires at least 257 case labels be allowed in a switch statement.

What is Fallthrough in switch statement Swift?

If you want your switch statement fall through or you want C style fallthrough feature then you can use fallthrough statement. This statement is used to forcefully execute the case present next after the matched statement even though the case is not matching the specified condition.


1 Answers

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

like image 132
Martin R Avatar answered Sep 23 '22 01:09

Martin R