Reputation: 5376
I have problem with declaring an Array and initialize it with many different generic items and in the same time use those items individually with generic support. Lets look at this:
protocol Item {
associatedtype ValueType: Any
var value: ValueType? { get set }
}
class ItemClass<T: Any>: Item {
typealias ValueType = T
var value: ValueType?
}
let intItem = ItemClass<Int>()
let stringItem = ItemClass<String>()
let array: [ItemClass<Any>] = [intItem, stringItem]
// Iterate over items and use `value` property as Any?
for item in array {
let val: Any? = item.value
// do something with val
}
// Individual usage with generic types support
let intValue: Int? = intItem.value
let stringValue: String? = stringItem.value
Why there is an error in array declaration like this:
Cannot convert value of type 'ItemClass<Int>' to expected element type 'ItemClass<Any>'
Upvotes: 0
Views: 48
Reputation: 299355
This entire approach is incorrect, and you can see that by the consuming code:
// Iterate over items and use `value` property as Any?
for item in array {
let val: Any? = item.value
// do something with val
}
In that "do something with val," what can you possibly do? There are no methods on Any
. If you're going to do something like as? T
, then you've broken the whole point of the types, because you don't mean "any". You mean "some list of types I know about." If you want "some list of types I know about," that's an enum with associated data, not a protocol with an associated type.
enum Item {
case string(String)
case int(Int)
var stringValue: String? {
guard case .string(let value) = self else { return nil }
return value
}
var intValue: Int? {
guard case .int(let value) = self else { return nil }
return value
}
}
let intItem = Item.int(4)
let stringItem = Item.string("value")
let array: [Item] = [intItem, stringItem]
// Iterate over items and use `value` property as Any?
for item in array {
switch item {
case let .string(value): break // Do something with string
case let .int(value): break // Do something with int
}
}
// Individual usage with generic types support
let intValue: Int? = intItem.intValue
let stringValue: String? = stringItem.stringValue
If, on the other hand, you really mean "any type," then you're not going to be able to put them in a collection without hiding the values in a box that gets rid of any information about that type (i.e. "a type eraser"). Which you need comes down to your actual use case. There isn't a single answer; it's going to be driven by how you want to consume this data.
But if you need as?
very much at all, you've done something wrong.
Upvotes: 1