`diff Swift C++`: `switch` on steroids

switch statement in C++, C# or Java is pretty much what we have had since the beginning of the 70s. Java and C# improved it a bit by disallowing automatic fallthrough between case statements and support of string values for matching.

But Swift greatly improves switch:

  1. Implicit fallthroughs between case bodies are not disallowed - they don't exist. There is no compiler error if you forget to put break. Instead the compiler will do it for you anyway.
  2. Taking a further step in the right direction if you want to enable fallthroughs between case statements, you have to do it explicitly by using fallthrough keybword.
  3. All switch statements must be exhaustive - that is they must be able to handle entire field of states of the value on which we are switching.
  4. Each case must contain at least one executable statement which means that all cases will at least do something, and if you don't want them to do anything you will have to explicitly do so.
  5. To specify more than one statement for a single switch you can separate them with commas. This is a nice syntatic improvement but also a necessary consequence of #4.
  6. You can do range matching by using .. (semi-open) and ... (closed) interval operators.
  7. Last but not least, apart from allowing integers, strings and floating point values in case, you can also do switching on tuples. Tuples switching allows:

    • Matching wildcards by using _ identifier and combining values and ranges within tuple values
    • Binding values from tuples in the case statement itself which allows you to use them immediately without having to query the tuple again
    • Using opotional where clause for additional conditions on tuple bound values.
    • Matching of enum values and their associated value tuples.

As you can see there is really a very tiny overlap between a valid switch statement in say C++ and a valid switch statment in Swift.

As a really small illustration, let's consider the example from my enum post. There I had an IpAddress enum representing either IPv4 or IPv6 addresses. It adopts Printable protocol from standard Swift library. I rewrote the implementation here to show no-implicit-fallthrough and no-break-needed:

//  Implementation of Printable protocol
var description: String {
    var desc: String
    switch self {
    case let .v4(b1, b2, b3, b4):
        desc = "\(b1).\(b2).\(b3).\(b4)"
    case let .v6(w1, w2, w3, w4, w5, w6, w7, w8):
        desc = "\(w1):\(w2):\(w3):\(w4):\(w5):\(w6):\(w7):\(w8)"
    }
    return desc
}

Here you can see (if you execute the code that is):

  1. no-implicit-fallthrough rule
  2. no-break-needed rule
  3. exhaust-all-values rule (only v4 and v6 are handled but that's all there is to this enum otherwise compiler would issue an error)
  4. pattern matching on enum values (v4 and v6) and their associated tuples.

Let's add a special case: we want to print ::1 when IPv6 loopback address is specified. With tuple pattern matching we can easily handle this:

//  Implementation of Printable protocol
var description: String {
    var desc: String
    switch self {
    case let .v4(b1, b2, b3, b4):
        desc = "\(b1).\(b2).\(b3).\(b4)"
    //    Specialized case has to appear before the generalized v6 case
    case let .v6(0, 0, 0, 0, 0, 0, 0, 1):
        desc = "::1"
    case let .v6(w1, w2, w3, w4, w5, w6, w7, w8):
        desc = "\(w1):\(w2):\(w3):\(w4):\(w5):\(w6):\(w7):\(w8)"
    }
    return desc
}

One important thing to note in the code above is that the special case has to appear before the general case. This is because switch will evaluate patterns in the order they were specified. This is reasonable and not at all surprising but it is something to keep in mind.

Author

Ivan Erceg

Software shipper, successful technical co-founder, $1M Salesforce Hackathon 2014 winner