0

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>'
4
  • 1
    That's because Swift generic types are not covariant. ItemClass<Any> and ItemClass<Int> are completely unrelated types. Commented Dec 19, 2018 at 12:43
  • So How I can declare Array this way so I have access to value: Any property of each items. I know each items should contain value property but since this is in array I can have different types of this property that should cast to Any. Commented Dec 19, 2018 at 12:44
  • If I declare array like this: let array: [ItemClass] = ... then I have the same problem. I know I'm casting ItemClass<Int> to ItemClass. I can not use let array: [Item] because of Protocol 'Item' can only be used as a generic constraint because it has Self or associated type requirements. I don't know what to do in this situation. Commented Dec 19, 2018 at 13:00
  • 1
    What you're trying to implement with ItemClass is called type erasure, but even that won't help, since even with a type erased AnyItem type, you'll only be able to store Items in an Array whose value property is of the same type. What you are trying to achieve isn't possible with your current setup. Why is item even a protocol? Why isn't it simply a generic struct in the first place? If your real problem is more complex than this, then include your real problem, not a contrived example. Commented Dec 19, 2018 at 13:36

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.