¿Por qué no es convertible a ?

Considera lo siguiente:

struct SomeStruct {} var foo: Any! let bar: SomeStruct = SomeStruct() foo = bar // Compiles as expected var fooArray: [Any] = [] let barArray: [SomeStruct] = [] fooArray = barArray // Does not compile; Cannot assign value of type '[SomeStruct]' to type '[Any]' 

He estado tratando de encontrar la lógica detrás de esto, pero sin suerte. Vale la pena mencionar si cambias la estructura a una clase, funciona perfectamente.

Siempre se podría agregar una solución alternativa y asignar cada objeto de fooArray y convertirlos al tipo Cualquiera, pero ese no es el problema aquí. Estoy buscando una explicación sobre por qué esto se comporta como es.

¿Puede alguien por favor explicar esto?

Esta pregunta SO me llevó a este problema.

Actualización de Swift 3

A partir de Swift 3 (específicamente la construcción que se incluye con Xcode 8 beta 6), los tipos de colección ahora pueden realizar conversiones bajo capó de colecciones de elementos de tipo valor a colecciones de elementos de tipo abstracto.

Esto significa que ahora se comstackrá lo siguiente:

 protocol SomeProtocol {} struct Foo : SomeProtocol {} let arrayOfFoo : [Foo] = [] let arrayOfSomeProtocol : [SomeProtocol] = arrayOfFoo let arrayOfAny : [Any] = arrayOfFoo 

Pre Swift 3

Todo esto comienza con el hecho de que los generics en Swift son invariantes, no covariantes. Recordando que [Type] es solo azúcar sintáctico para Array , puedes abstraer las matrices y Any para ver mejor el problema.

 protocol Foo {} struct Bar : Foo {} struct Container {} var f = Container() var b = Container() f = b // error: cannot assign value of type 'Container' to type 'Container' 

De manera similar con las clases:

 class Foo {} class Bar : Foo {} class Container {} var f = Container() var b = Container() f = b // error: cannot assign value of type 'Container' to type 'Container' 

Este tipo de comportamiento covariante (upcasting) simplemente no es posible con generics en Swift. En su ejemplo, Array se ve como un tipo completamente no relacionado con Array debido a la invarianza.

Sin embargo, las matrices tienen una excepción a esta regla: pueden tratar silenciosamente las conversiones de tipos de subclases a tipos de superclase bajo el capó. Sin embargo, no hacen lo mismo al convertir una matriz con elementos de tipo de valor a una matriz con elementos de tipo abstracto (como [Any] ).

Para hacer frente a esto, debe realizar su propia conversión elemento por elemento (ya que los elementos individuales son covariantes). Una forma común de lograr esto es mediante el uso de map(_:) :

 var fooArray : [Any] = [] let barArray : [SomeStruct] = [] // the 'as Any' isn't technically necessary as Swift can infer it, // but it shows what's happening here fooArray = barArray.map {$0 as Any} 

Una buena razón para evitar una conversión implícita bajo el capó aquí se debe a la forma en que Swift almacena los tipos abstractos en la memoria. Se usa un ‘Contenedor Existencial’ para almacenar valores de un tamaño arbitrario en un bloque fijo de memoria, lo que significa que la asignación del montón caro puede ocurrir para valores que no caben dentro de este contenedor (permitiendo solo una referencia a la memoria que se almacenará en este contenedor en su lugar).

Por lo tanto, debido a este cambio significativo en la forma en que la matriz ahora está almacenada en la memoria, es bastante razonable rechazar una conversión implícita. Esto hace que el progtwigdor le explique explícitamente que tiene que moldear cada elemento de la matriz, lo que causa este cambio (potencialmente costoso) en la estructura de la memoria.

Para obtener más detalles técnicos sobre cómo funciona Swift con los tipos abstractos, consulte esta fantástica charla de la WWDC sobre el tema . Para leer más sobre la varianza de tipo en Swift, consulte esta gran publicación de blog sobre el tema.

Finalmente, asegúrese de ver los comentarios de @dfri a continuación sobre la otra situación en la que las matrices pueden convertir implícitamente tipos de elementos, es decir, cuando los elementos se pueden unir a Objective-C, la matriz los puede hacer implícitamente.

Swift no puede convertir automáticamente una matriz que contenga tipos de valores y tipos de referencia. Simplemente asigne la matriz al tipo que necesita:

fooArray = barArray.map ({$ 0}) // Hace la comstackción