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!
Related