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