Search code examples
swiftprotocols

Protocol conforming to type with associated value


I've got the following snippet:

protocol MyProtocol: Identifiable where ID == UUID {
    var id: UUID { get }
}


var test: [MyProtocol] = []

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

Why doesn't this work? Shouldn't the where ID == UUID remove the ambiguity the error is concerned with? Am I missing something here?

I think this question is similar to this one: Usage of protocols as array types and function parameters in swift

However, I would have assumed that adding where ID == UUID should fix the problem? Why is that not the case?

Thanks!

Edit

So, this problem has occurred while experimenting with SwiftUI and struct data models. I've always used classes for any kind of data model but it seems like SwiftUI wants to get you to use structs as often as possible (I still don't see how that's realistically possible but that's why I'm experimenting with it).

In this particular case, I tried to have a manager that contains structs that all conform to MyProtocol. For example:

protocol MyProtocol: Identifiable where ID == UUID {
    var id: UUID { get }
}

struct A: MyProtocol { // First data model
    var id: UUID = UUID()
}

struct B: MyProtocol { // Second data model
    var id: UUID = UUID()
}

class DataManager: ObservableObject {
    var myData: [MyProtocol]
}

...

I don't actually have to declare Identifiable on MyProtocol but I thought it would be nicer and cleaner.


Solution

  • Because this is not a current feature of Swift. Once there is an associated type, there is always an associated type. It doesn't go away just because you constrain it. And once it has an associated type, it is not concrete.

    There is no way to "inherit" protocols this way. What you mean is:

    protocol MyProtocol {
        var id: UUID { get }
    }
    

    And then you can attach Identifiable to structs that require it:

    struct X: MyProtocol, Identifiable {
        var id: UUID
    }
    

    (note that no where clause is required.)

    There is no Swift feature today that allows you to say "types that conform to X implicitly conform to Y." There is also no Swift feature today that allows for an Array of "things that conform to Identifiable with ID==UUID." (That's called a generalized existential, and it's not currently available.)

    Most likely you should go back to your calling code and explore why you require this. If you post the code that iterates over test and specifically requires the Identifiable conformance, then we may be able to help you find a design that doesn't require that.