How to write a generic that support casting from Any (like Array<Any> = Array<Int>() )

generics

(Alain) #1
1:    class MyArray<T> {}
2:
3:    var a: Array<Any> = Array<Int>()
4:    var b: MyArray<Any> = MyArray<Int>()    // Cannot assign value of type 'MyArray<Int>' to type 'MyArray<Any>?'

What am i missing?


(Quinn “The Eskimo!”) #2

Can you clarify the problem here? I’m not sure what you’re asking and running your code didn’t work the way you described [1]. Perhaps you could post an update with more details.

ps When you post code it’s best to format it as code so that it’s easier to read, has line numbers that folks can refer to, and so on.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

[1] I put your code into an Xcode 10.1 test project and it fails on the line before the line you highlighted.

var a: Array = Array()
               ^~~~~~~
error: cannot convert value of type 'Array<_>' to specified type 'Array'

Moreover, the line you highlighted does not fail for me.


(Alain) #3

I edited my post, sorry... my first post, I thought it was preformatted.

Thank you for the tips @eskimo :smile:


(Poltora Ivana) #4

That's because custom generic structs are invariant. For stdlib there's some magic involved that makes built-in collections (Arrays, Dictionaries, etc) covariant.


(Vincent Duvert) #5

In your example, MyArray is a class type. If it allows mutations (has a append method for instance), casting it would allow code like this to compile:

var x = MyArray<Int>()
var y: MyArray<Any> = x
x.append("oops" as Any) // Oops, we put a String into a MyArray<Int> instance. What should happen?

(I believe that Java, whose arrays are classes instances and allows covariance, has this problem; this code will compile but fail at runtime)

Swift’s arrays are struct types, so the cast is fine since the conversion “copies” the array:

var x = Array<Int>() 
var y: Array<Any> = x 
y.append("ok" as Any) // This modifies y but not x, so x still only contains Ints

So in theory, covariance could be supported for user-defined container types, as long as they are structs or immutable classes, but as @PoltoraIvana mentioned that’s not currently supported except for some built-in stdlib types.