Rob Gorman
Rob Gorman

Reputation: 3694

Switch statement in Swift allows duplicate case values. Is this feature or bug?

The swift switch statement (tested in Xcode 6.1) allows repeated switch values. See code below which compiles and runs without error. Apparently, the first matching value is the executed case. Subsequent cases are ignored. Is this a bug or feature? If a feature, how is it useful?

class SwitchTest{

enum E {
  case one, two, three
  }

func test() {
  switchStmt(E.one)  // outputs: "one, two"
  switchStmt(E.two)  // outputs: "one, two"
  switchStmt(E.three) //outputs: "three, two"
}

func switchStmt(e : E)
{
  switch (e){
  case .one, .two:
    println("one, two")
  case .three, .two:
    println("three, two")
  case .one:
    println("one")
  case .two:
    println("two")
  }
}
}

Upvotes: 1

Views: 2961

Answers (3)

paxdiablo
paxdiablo

Reputation: 882366

It's only a bug if the documentation says it does one thing but it actually does another. Anything where the documentation remains silent on the matter is technically only a disagreement between implementer and user.

However, in this case, the documentation does not remain silent on the matter, it states quite clearly (my emphasis):

It then executes an appropriate block of code, based on the first pattern that matches successfully.

The reason it's like this is probably to do with the fact that, unlike C, you're not limited to simple matches. You can provide, in the case stanza, single values, lists of single values, ranges, tuples, bindings, additional clauses like where, and the mandatory default case if you haven't already trapped all possible values of the type.

In that sense, it's quite logical. It's just a process of comparing the check value against every case stanza in order and executing the first match, regardless of whether it would match those below as well.

In terms of usefulness, that sequential nature will come in handy if you wanted to specify specific cases at the top and more general ones below, such as with the code:

switch num {
    case 2, 3, 5, 7, 11, 13, 17, 19:
        println("prime, 1 <= x <= 20");
    case 1...20: // don't need 1, 4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20
        println("non-prime, 1 <= x <= 20");
    default:
        println("x < 1 or x > 20");
}

With the sequential checking nature and specific-to-general-case flow, you can simplify the later cases.

Upvotes: 5

Keith Knauber
Keith Knauber

Reputation: 802

This should most definitely be a syntax error! This will be the source of innumerable copy/paste errors.

Upvotes: 0

nhgrif
nhgrif

Reputation: 62062

This is a feature--not a bug. It's usefulness is made clear by less simple cases.

Consider this example from the official documentation:

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    println("(0, 0) is at the origin")
case (_, 0):
    println("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    println("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    println("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    println("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}

Consider that if somePoint had a value of (0,0), all of the cases would be true. And if the tuple were specifically (Int, Int), the final case: case (-2...2, -2...2): could potentially be fully written out (25 combinations) in a comma separated list, more similar to your example. We should want that to disallow our specific case of case (0, 0):, should we?

If we only define our cases in the way we're used to with a standard C-style switch statement, it doesn't make much sense... but then, giving multiple values in a single case isn't a C-style switch statement either.

The example in your question is a bad example of a good feature of the Swift switch statement.


It should be noted though that if we were to reorder the cases here, we could easily end up with switch where it's impossible for one of the cases to ever execute. And it might be nice to get a warning generated if we build this sort of switch statement (for example, what happens if we move case (0, 0): right before the default).

But, consider that the following generates no warning:

for i in 2...1 {
    println(i)
}

It should, however, be noted that this will crash: fatal error: Can't form Range with end < start.

The point is, Xcode won't catch absolutely everything, so I'm not sure why we'd pick on switch statements. A simple case like the example in your question might seem easy, but when we consider the totality of the complexity of Swift switch statements, I'm not sure I want a lot of time spent analyzing my switch statements like this.

Upvotes: 1

Related Questions