The (Switch) Case of the Missing Binding — Erica Sadun


Here’s a cool little challenge brought up this morning by a friend. Consider the following code:

switch foo {
  case .a: return "a"
  case .b(let str) where str.hasPrefix("c"), .c: return "c"
  case .b: return "b"
}

It won’t compile.

When you bind a symbol for one pattern, you must bind that symbol for every pattern in a case. This prevents you, for example, from binding str in one pattern and then attempting to use str in the shared case body. For example, consider this case. What would you expect to happen when foo is .c?

func switchTheFallthroughOrder(foo: Foo) -> String {
    switch foo {
    case .a: return "a"
    case .b(let str) where str.hasPrefix("c"), .c:
        // Using `str` here is bad!
        print(str)
        return "c"
    case .b: return "b"
    }
}

Despite my first knee-jerk refactoring, moving out the .c case to use fallthrough doesn’t work. Again, this is because str is not bound for .c and could be used in the successive case body:

However, as Greg Titus pointed out, if you switch the order to use the binding case first with fallthrough, Swift knows at compile time that the binding won’t carry on beyond that scope. This resolves the error, since str is only used in the where clause to narrow the pattern matching:

Further, when using bindings in case tests, a waterfall approach where the bound items are used before fallthrough can extend through multiple steps with the blessing of the compiler:

case .widest(let first, let second) where first.satisfiesACondition():
    // can use `first`, `second` here
    fallthrough
case .medium(let second) where second.satisfiesAnotherCondition():
    // can use `second` here even if it was bound 
    // via `widest` above via fallthrough
    fallthrough
case .narrowest: return someValue

My thanks to Greg Titus for figuring this all out!



Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img