0

I'm trying to wrap my head around typealias and associatedtype in Swift and I'm struggling with a specific case.

Basically I want to have a protocol which defines a method that takes in a parameter that conforms to a protocol and every other protocol that conforms to that protocol (a bit confusing, yes I know). But basically I just want to allow multiple layers of conformance.

So, this is the closest I could get (this compiles):

protocol Bar {}
protocol BarVariant: Bar {}
struct BarVariantImpl: BarVariant {}


protocol Foo {
    associatedtype T: Bar
    func update(bar: T)
}

struct FooImpl: Foo {
    typealias T = BarVariantImpl
    func update(bar: T) {}
}


FooImpl().update(BarVariantImpl())

The problem with the above code is that the typealias T = BarVariantImpl pins down the parameter type to the concrete implementation. I want to allow parameters that conform to BarVariant instead. My first thought was to do typealias T: BarVariant instead, but this does not compile.

I'm running Swift 2.2.1.

Steffen D. Sommer
  • 2,896
  • 2
  • 24
  • 47
  • 1
    This is a limitation with `associatedtype` – once you constrain them to a given protocol, you cannot satisfy them with an abstract type. See [this Q&A](http://stackoverflow.com/q/37360114/2976878) and [this bug report](https://bugs.swift.org/browse/SR-1581) on it. – Hamish Aug 03 '16 at 09:42
  • Although note the expected syntax would be `typealias T = BarVariant` (or just `func update(bar: BarVariant)`), rather than `typealias T : BarVariant`. – Hamish Aug 03 '16 at 09:48
  • Thanks for the reply @Hamish. From the given Q&A link, wouldn't I be able to solve it just be using `struct FooImpl: Foo {` and removing the `typealias`. As far as I see, this seems to do the trick? It seems to satisfy the protocol and restrict the parameter to `BarVariant`. – Steffen D. Sommer Aug 03 '16 at 11:02
  • 1
    Sure – the only thing to note with generics is that each instance of `FooImpl` will be restricted to having a *single* concrete type that conforms to `BarVariant` (i.e the concrete type of `T` that you initialise the instance with will be used for the method input). This would be different to simply having a method input of type `BarVariant`, which would accept *any* input that conforms to that protocol. If you want this kind of behaviour instead, you'll have to use a type erasure, as also shown in the linked answer. – Hamish Aug 03 '16 at 11:07
  • I might be misunderstanding, but I don't believe that this is the case. I don't initialise `FooImpl` with a concrete type, but I use the abstract type `BarVariant`. This allows me to call the `update` method with any type that conforms to `BarVariant`. E.g. the concrete types `struct BarVariantImpl: BarVariant {}` and `struct BarVariantImpl2: BarVariant {}` would both be accepted in the call to `update`. – Steffen D. Sommer Aug 03 '16 at 11:13
  • 1
    That's because you're doing the call on a new instance each time, which will infer `T` to be the concrete type of whatever you pass into `update`. Try creating a new instance and calling the method on separate lines. Generics also have the same limitation as associated types, once constrained they cannot accept an abstract type. This all stems from the fact that protocols don't necessarily conform to themselves (so `BarVariant` cannot be accepted as a type that conforms to `Bar`) – see [this Q&A](http://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself). – Hamish Aug 03 '16 at 11:19
  • You are completely right. Interesting limitation. Thanks a lot for helping out. – Steffen D. Sommer Aug 03 '16 at 11:24

0 Answers0