Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch on Array type

Overview

I would like to use switch statement by matching array type. I have the following classes.

Class:

class A {}    
class B : A {}

Switch on single value works:

let a : A = B()

switch a {
case let b as B:
    print("b = \(b)")
default:
    print("unknown type")
}

Switch on array (Compilation Error):

let aArray : [A] = [B(), B()]

switch aArray {
case let bArray as [B] :
    print("bArray = \(bArray)")
default:
    print("unknown type")
}

Error:

Downcast pattern value of type '[B]' cannot be used

Note: Tested on Swift 4

Question:

  • How can I achieve this ?
like image 461
user1046037 Avatar asked Mar 21 '18 12:03

user1046037


1 Answers

In Swift 4.1, you get a better error message (thanks to #11441):

Collection downcast in cast pattern is not implemented; use an explicit downcast to '[B]' instead

In short, you're hitting a bit of the compiler that isn't fully implemented yet, the progress of which is tracked by the bug SR-5671.

You can however workaround this limitation by coercing to Any before performing the cast:

class A {}
class B : A {}

let aArray : [A] = [B(), B()]

switch aArray /* or you could say 'as Any' here depending on the other cases */ {
case let (bArray as [B]) as Any:
  print("bArray = \(bArray)")
default:
  print("unknown type")
}

// bArray = [B, B]

Why does this work? Well first, a bit of background. Arrays, dictionaries and sets are treated specially by Swift's casting mechanism – despite being generic types (which are invariant by default), Swift allows you to cast between collections of different element types (see this Q&A for more info).

The functions that implement these conversions reside in the standard library (for example, Array's implementation is here). At compile time, Swift will try to identify collection downcasts (e.g [A] to [B] in your example) so it can directly call the aforementioned conversion functions, and avoid having to do a full dynamic cast through the Swift runtime.

However the problem is that this specialised logic isn't implemented for collection downcasting patterns (such as in your example), so the compiler emits an error. By first coercing to Any, we force Swift to perform a fully dynamic cast, which dispatches through the runtime, which will eventually wind up calling the aforementioned conversion functions.

Although why the compiler can't temporarily treat such casts as fully dynamic casts until the necessary specialised logic is in place, I'm not too sure.

like image 116
Hamish Avatar answered Nov 14 '22 07:11

Hamish