Home > front end >  Return Class conform to protocol
Return Class conform to protocol

Time:12-26

I have a protocol that represent brand for example

I have two class in this example, Jbl and Columbo that conform to Brand protocol

The builder build some stuff and finally return the name of Jbl or Columbo class (The build stuff is simplified with random in code sample)

In this sample code we can expect randomly Jbl Brand or Columbo Brand in the print result

But this cannot compile with error : Protocol 'Brand' can only be used as a generic constraint because it has Self or associated type requirements

protocol Brand: AnyObject, Hashable {
    var name: String { get }
}

extension Brand {
    func hash(into hasher: inout Hasher) {
        hasher.combine(name)
    }

    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.name == rhs.name
    }
}

class Jbl: Brand {
    var name: String { "Jbl" }
}

class Columbo: Brand {
    var name: String { "Columbo" }
}

class Builder {
    func build() -> Brand {    // Protocol 'Brand' can only be used as a generic constraint because it has Self or associated type requirements
        if .random() {
            return Jbl()
        } else {
            return Columbo()
        }
    }
}

var builder = Builder()
print(builder.build().name)

CodePudding user response:

The problem is that for build() you are trying to return a type of Brand, but Hashable has Self requirements as part of its definition. This is why you get the error about "Self or associated type requirements".

A possible solution is to make Brand not Hashable itself, but any class which conforms to Brand and Hashable will get the Hashable implementation for Brand:

protocol Brand: AnyObject {
    var name: String { get }
}

extension Brand where Self: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(name)
    }

    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.name == rhs.name
    }
}

However, accessing build() will not give you a hashValue, since only classes which conform to Brand and Hashable have hashValue.

To fix this, conform Jbl and Columbo to the Hashable protocol. Then add a function like this in the Builder class for example:

func getHashValue() -> Int {
    if .random() {
        return Jbl().hashValue
    } else {
        return Columbo().hashValue
    }
}

Called like:

print(builder.getHashValue())
// Prints: -8479619369585612153

Obviously this hash changes each time the app is relaunched, so don't rely on it for persisting data.

And just for style reasons: you may prefer to use the following and make the classes just conform to HashableBrand:

typealias HashableBrand = Brand & Hashable
  • Related