Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift array of generics

How do I create an array of generics? Example:

struct Thing<Any> {
}

let intThing = Thing<Int>()
let stringThing = Thing<String>()

// This line doesn't compile
// Cannot convert value of type 'Thing<Int>' to expected type 'Thing'
let things: [Thing] = [intThing, stringThing]

How do I declare a generic of any type (something like Thing<?> or Thing<Any>)?

like image 821
Steve Kuo Avatar asked Mar 05 '16 18:03

Steve Kuo


People also ask

Can array have different data types in Swift?

Here, [Int]() specifies that the empty array can only store integer data elements. Note: In Swift, we can create arrays of any data type like Int , String , etc.

What is T type Swift?

The placeholder type T is used in the function declaration. It tells Swift that this function can find any item in any array, as long as the foundItem and items in the array are of the same type. This makes sense — you want to look for a T value in an array of T values.

What is difference between generic and any Swift?

Generics and Any are often used for similar purposes, yet they behave very differently. In languages without generics, you typically use a combination of Any and runtime programming, whereas generics are statically checked at compile time.


2 Answers

You can do this:
let things: [Any] = [intThing, stringThing]

Thing is not a valid type on it's own. Thing<String> is a type and Thing<Int> is an other type and you can't mix different type within an array.

Have you notice that even let things: [Thing] doesn't compile?

I think that what you are probably trying to do would be better server with an enum with associated values.


struct MyStruct<T> {

    let property: T
    init(property: T) {
        self.property = property
    }
}

enum Thing {
    case int(Int)
    case string(String)
    case intStruct(MyStruct<Int>)
}

// creation
let myInt = Thing.int(2)
let myString = Thing.string("string")
let myIntStruct = Thing.intStruct(MyStruct<Int>(property: 3))

// down side is that you need a 'case' clause to retrieve the data
switch myIntStruct {
case let .int(value):
    print("have a int: \(value)")
case let .string(value):
    print("have a string: \(value)")
case let.intStruct(value):
    print("have a intStruct\(value)")
}

// An array of Thing
let things: [Thing] = [myInt, myString, myIntStruct]

nice article about advance enum trick here
In this file about wrapping Plist data into a single structure, there is the use of an enum for the EntityType.


You can associate anything to an enum value, but it needs to be a fully qualified type. So you can't use MyStruct because it is not fully qualified, you need to use MyStruct<Int> to fully qualify. This means that you would need to create a case for every generic type you want to use with MyStruct.

That is as far as you can go with your generic approach.
I'm not sure what you are trying to do. But if you want to achieve some kind of polymorphism that relies on methods that operate on T but don't use T as input nor output you should make your Thing implement a protocol and make an array of that protocol and call your polymorphic method on that protocol.

like image 124
Vincent Bernier Avatar answered Sep 21 '22 20:09

Vincent Bernier


You can use an enum

enum Thing {
    case Text(String)
    case Integer(Int)
}

Now you can create a Thing containing a String or an Int

let thingWithText = Thing.Text("Hello world")
let thingWithInt = Thing.Integer(123)

And you can put them inside an array of Thing(s) (no generics involved).

let things = [thingWithText, thingWithInt]

Finally you can process the values inside the array this way

things.forEach { (thing) -> () in
    switch thing {
    case .Text(let text): print(text)
    case .Integer(let integer): print(integer)
    }
}
like image 36
Luca Angeletti Avatar answered Sep 22 '22 20:09

Luca Angeletti