Allow properly constrained sub-protocols to be used as existentials

This has been discussed back in 2016, and a solution to fill in the type hole with associated types was implemented in SE-0142. However, the following is still illegal:

protocol Identifiable {
    
    associatedtype ID
    
    var id: ID { get }
    
}

protocol MyProtocol: Identifiable where ID == String {
    
}

struct MyStruct: MyProtocol {
    
    var id: String {
        "..."
    }
}

let myExistential: MyProtocol = MyStruct()

Specifically, the last line raises the compiler diagnostic:

error: protocol 'MyProtocol' can only be used as a generic constraint because it has Self or associated type requirements

I'll admit I haven't done too much thinking into the impact of this, but it seems like the where clause in MyProtocol removes the associated type requirement, and ID in MyProtocol should be treated like a typealias rather than associatedtype. So, MyProtocol should be allowed to be used as an existential protocol. Thoughts?


There is also this similar syntax for defining MyProtocol that should also fill in the type hole:

protocol MyProtocol: Identifiable {
    associatedtype ID = String
}
protocol MyProtocol: Identifiable {
    var id: String { get }      
}
2 Likes

For reference, it was also discussed last week: Why can't protocol which inherits from PAT but constrains its associated types be used as existential?

4 Likes

As I said in the recent versions of this thread, we shouldn't have to create a new protocol to take advantage of existentials.

func myFunction(_ x: Identifiable<where ID == String>) -> Int { /*...*/ }

should be enough.

2 Likes

The constrained protocol might typically be created for its own sake, e.g. describing a type from the domain, and not just as a constrained version of the generic protocol.
The proposal would allow writing functions using that protocol as existential.

It still makes to write functions for specific specializations of the generic protocol, of course.