Protocol type cannot conform because only concrete types can conform to protocols

protocol BaseProtocol { ... }

protocol SubProtocol: BaseProtocol { ... }

open class BaseClass<Type> where Type: BaseProtocol

class SubClass: BaseClass<SubProtocol> // error

This gives the error:

Protocol type 'SubProtocol' cannot conform to 'BaseProtocol' because only concrete types can conform to protocols

Even though in the SubProtocol definition, it already conforms to BaseProtocol. Is this the same issue of a protocol conforming to itself?

Pretty much, try

class SubClass<T>: BaseClass<T> where T: SubProtocol

It's because Type is a generic parameter, not a concrete type and you are trying to make it, a non-concrete type conforms to SubProtocol, which is also not a concrete type.

SubProtocol also doesn't "conform" to BaseProtocol, it inherits from or extends it.

This also smells of being a parallel class/protocol hierarchy. To be avoided.

Could the generics restriction here on using concrete types be lifted by using the new reverse generics? E.g. class SubClass: BaseClass<some SubProtocol>

Why would it? That would dtill not be a concrete type. It has to be a class, a struct or an enum to satisfy being concrete

Technically, you can, but it’s not a trivial addition (and likely need to go through SE), and very, very likely not what you want.

Should we really add it, at best

class SubClass: BaseClass<some SubProtocol>

would just be shortened version of

class SubClass: BaseClass<SomeConcreteTypeYouDontWantToTypeItOut>

Downstream users can only use SubClass, which is a subclass of BaseClass<SomeConcreteTypeYouDontWantToTypeItOut>, and nothing else. If this is what you want, just type the actual concrete type name out.

On the other hand,

class SubClass<T>: BaseClass<T> where T: SubProtocol

Leave T as a generic parameter. Downstream users can use SubClass<Int> and SubClass<String> which are subclasses of BaseClass<Int> and BaseClass<String> respectively, given that you conform Int and String properly.

1 Like

It's not that I want to hide the concrete type because I don't want to type it out, it's because somewhere further in the chain of code making this use generic constraints runs into the issue “Protocol can only be used as a generic constraint because it has Self or associated type requirements.”, so starting here with a reverse generic would work. It seems technically possible to add and there might be some utility out of it.

Reverse Generic is not as powerful as you'd think.

The premise of Generic is that someone (implementor, user), doesn't need to know the concrete type.

  1. If implementor doesn't need to know, you can use forward generic for that.

    func foo<T: Foo>(_: T) { ... }
    foo(4)
    

    So implementor of foo doesn't know what T is, only that the user supply something that conforms to Foo.

  2. If the user doesn't need to know, you can use reverse generic.

    func foo() -> some Foo { ... }
    let a = foo()
    

    Then the user won't know what the type of a is, just that it conforms to Foo.

  3. If both sides know what T is, they can just use concrete type.

  4. If neither side knows what T is, well, that'd be bad.

All the concrete types must be decided somewhere along the line to instantiate the variables and pass them around. That also apply to the generic struct, class, etc.

They're not some tools to silent the compiling error.

You description seems to lean toward the first case, which I already suggested the syntax above.

2 Likes