Tonnie
Tonnie

Reputation: 169

How to cast 'Any?' to a struct with flexible generic properties?

I'm using a 3rd party library that in my use case is handing me down a '[Any?]' array. This array contains equatable struct objects which are defined as follows:

struct SplitValue<L: Equatable, R: Equatable> {
    public var left: L?
    public var right: R?

    public init(left: L?, right: R?){
        self.left = left
        self.right = right
    }
}

extension SplitValue: Equatable {
    public static func ==(lhs: SplitValue, rhs: SplitValue) -> Bool {
        return lhs.left == rhs.left && lhs.right == rhs.right
    }
}

And this is a simple example of the array I'm dealing with:

var values = [Any?]()
values.append(SplitValue<String, Int>(left: "a", right: 12))
values.append(SplitValue<Double, String>(left: 33.1, right: "b"))

Now let's say I cannot predict which value types are found in the SplitValue.left and SplitValue.right properties. It could be a String, Int, NSManagedObject, or anything else equatable. All I have is the values array.

Next I want to loop through this array and read out the left and right values. For this I believe I have to cast 'Any?' to 'SplitValue' because the following doesn't work:

print(values.first!.left)
// error: value of type 'Any?' has no member 'left'

I also tried things like this, but that obviously doesn't work either:

print(values.first! as SplitValue<Any, Any>)
// error: type 'Any' does not conform to protocol 'Equatable'

So the problem is that I can't cast 'Any?' to the right type since I don't know what it will be. What might be the solution to this, preferably without having to change the definition of the struct or values array?

Upvotes: 1

Views: 407

Answers (1)

Sweeper
Sweeper

Reputation: 271775

What you can do is to create a protocol without the generic types and have SplitValue implement that:

protocol AnySplitValue {
    var l: Any? { get }
    var r: Any? { get }
}

Then, conform SplitValue to the protocol:

extension SplitValue: AnySplitValue {
    var l: Any? {
        return self.left
    }

    var r: Any? {
        return self.right
    }
}

Now, you can do this to print all the left values out:

values.forEach { print(($0 as! AnySplitValue).l!) }

We have kind of "erased" the generic types!

Upvotes: 3

Related Questions