Protocol problem

What's wrong with this code:


protocol A {
}

protocol B {
    var aType: A { get }
}

struct TestB : B {
   
    struct AType : A {
    }
   
    var aType: AType
   
    init() {
        atype = AType()
    }
}


It won't compile since aType in the struct has 'non-matching type TestB.AType'. But TestB.AType implements protocol A, and that's all that should be required.


I've tested this under Swift 1.2 and 2.0. Hopefully I'm just misunderstanding something.

The protocol type A is a distinct type from TestB.AType. They aren't interchangable. While the compiler will implicitly convert from TestB.AType to A for you, it can't do this easily to the return value of a dynamically dispatched method—such as a protocol method, or in your case, a read-only property—since it doesn't know the original type it's converting from. It would have to start generating extra methods/properties that wrap the ones you write which do the conversion, which starts to get a bit too magical.


To get around this, you can make the protocol more generic so that each conforming type can specify its own property type:


protocol B {
    typealias AType: A
    var aType: AType { get }
}


but this means you can no longer use B as a type and can only use it as a contraint for generics (or rather, you can use it as a type, but you won't be able to access the aType property).


The other option is to provide your own wrapper around your property to make it conform to the protocol:


struct TestB : B {

    struct AType : A {
    }

    var _aType: AType

    var aType: A {
        return _aType
    }

    init() {
        _aType = AType()
    }
}

Swift doesn't allow a type to use methods which return a subclass-of-expected-type or a protocol-conforming-type to meet the requirements of a protocol (at least as of 2.0).


If you change the type of the aType property to match the protocol, your code compiles.


protocol A {
}

protocol B {
    var aType: A { get }
}


struct TestB : B {
   
    var aType: A
   
    struct AType : A {
    }
   
    init() {
        aType = AType()
    }
}


If you need to interact with the AType struct as it's original type within TestB, you could have a separate property declared as AType which gets returned by the protocol-required version.

Thanks for your answers, although I'm strugging to get my head around understand what, exactly, isn't allowed.


Protocol B requires a property that conforms to A.

Struct B provides a property that conforms to A.


While I'll obviously accept that I can't do that, it would be nice to have a concise statement that describes the limitation. What I'm doing doesn't seem that unusual to me.

In your original code:


Protocol B requires a property of the explicit type "any type that conforms to protocol A"

Struct B provides a property of a different explicit type "StructA", which happens to conform to A

Protocol problem
 
 
Q