2

I'm just curious if its possible to do something like the following

protocol Data { }

class A: Data { }

class B: Data { }

class Generic<T> { }

class doStuff {

    func prepareToDoStuff() {
        self.doTheStuffWithGenerics([Generic<A>(), Generic<B>])
    }

    func doTheStuffWithGenerics<T: Generic<Data>>(_ generics: [T]) {

    }
}

Currently my compiler is telling me no saying

"cannot convert value of type 'Generic[A]' to expected element type 'Generic[Data]'"

any thoughts? solutions?

trooper
  • 4,444
  • 5
  • 32
  • 32
Genhain
  • 1,937
  • 1
  • 19
  • 38
  • Related (dupe?): [How do I store a value of type Class in a Dictionary of type \[String:Class\] in Swift?](http://stackoverflow.com/q/38590548/2976878) & [Swift generic coercion misunderstanding](http://stackoverflow.com/q/41976844/2976878) – generics are invariant in Swift. You probably want a type-eraser. – Hamish Mar 28 '17 at 09:07
  • @Hamish I am aware of using type-erasure for generic protocols, but a generic class or struct im a bit lost... Do you have any resource or know yourself how to demonstrate this concept? – Genhain Mar 29 '17 at 02:25
  • I'm not sure I understand you – *both* Q&As I linked to are talking about generic classes (where they want to convert a `Generic` to a `Generic` where `SomeClass` conforms to `SomeProtocol`). Here's [an example gist](https://gist.github.com/hamishknight/597718c80411724dc40815b8ac6a6d33) applied to your exact example, using a type eraser implementation that I show in [my answer here](http://stackoverflow.com/a/41982563/2976878). – Hamish Apr 05 '17 at 14:15

4 Answers4

5

Others have explained why doesn't work. So I'll just focus on the workaround. The way Apple itself solves this issue (take a look at Collection protocol for example) is using protocols. It's common to move your logic to the Generic class, or use extension classes on your protocols.

For example:

protocol Data { }
protocol GenericDataType {
    func doStuff()
}

class A: Data { }
class B: Data { }


class Generic<T: Data>: GenericDataType {
    func doStuff() {
        print("Processing element")
    }
}

class doStuff {

    func prepareToDoStuff() {
        let array: [GenericDataType] = [Generic<A>(), Generic<B>()]
        self.doTheStuffWithGenerics(array)
    }

    func doTheStuffWithGenerics(_ generics: [GenericDataType]) {
        for element in generics {
            element.doStuff()
        }
    }
}
redent84
  • 18,901
  • 4
  • 62
  • 85
1

The feature you a looking for is what is usually called the variance of a generic types. The variance of a type describes how subtype relationships between different instantiations of the type vary with its type parameters.

Java has support for this through its type wildcard construct. Using this construct your example could be written like this:

// "? extends Data" means: Some specific but unknown type which extends Data
func doTheStuffWithGenerics(_ generics: [Generic<? extends Data>]) {

}

func prepareToDoStuff() {
    // This would be okay because both A and B are *some type that extend Data*
    self.doTheStuffWithGenerics([Generic<A>(), Generic<B>])
}

Judging from the other answers it doesn't sound like Swift supports any kind of variant generic types (yet).

Explanation of wildcards

The type ? extends Data that is the type of the parameter to Generic has some unusual properties:

  • When you read from a field with this type you will get an object of type Data.
  • It is impossible to write to a field of this type! Because the actual field has some unknown type the compiler can never know that you write the right can kind of object to it.
Lii
  • 11,553
  • 8
  • 64
  • 88
0

You wouldn't be able to do something like that since A, B, and Data are distinct types (not objects). It wouldn't make sense to able to convert Generic<A> to Generic<Data> - as they both have distinct type parameters.

hvasam
  • 304
  • 4
  • 12
0

Swift is static typed and it means compiler requires to know exactly every variables' concrete type at compile time.

And from this viewpoint, Generic<A>, Generic<B> and Generic<Data> are three different types. This is not same as A,B and Data.

Since A, B conform to Data so you can cast [A(), B()] to type [Data] ( let a = [A(), B()] as [Data] ).

But there is not such "conform to" relationship between Generic<A>, Generic<B> and Generic<Data>. Thus [Generic<A>(), Generic<B>()] can not be converted to type [Generic<Data>], it can only be converted to [Any] and obviously Any can not be converted to expected element type Generic<Data>

Nandin Borjigin
  • 2,094
  • 1
  • 16
  • 37