This morning I was playing with a couple of model objects, trying to find a way to make them conform to Equatable. That’s not a big deal, you might say… well, in this case I wanted to make the protocol those objects implement, not the actual objects, conform to Equatable, so things got a little tricky.
Let’s say this is where I started:
protocol Drinkable { var name: String { get } var alcohol: Float { get } } struct Beer: Drinkable { let name: String let alcohol: Float }
Now, I want everything that conforms to Drinkable to be Equatable. So this was my first attempt:
extension Drinkable: Equatable { } func == (lhs: Drinkable, rhs: Drinkable) -> Bool { return lhs.name == rhs.name && lhs.alcohol == rhs.alcohol } let guiness = Beer(name: "Guiness", alcohol: 6) let anotherGuiness = Beer(name: "Guiness", alcohol: 6) let singha = Beer(name: "Singha", alcohol: 5) print(guiness == anotherGuiness) print(guiness == singha)
Nah-ha. “Extension of protocol ‘Drinkable’ cannot have an inheritance clause”
The solution? Turning things around little bit (would this qualify as “inverting the dependency”, haha-lol?) and enforce Equatable to respond to == when the type implementing Equatable is Drinkable. In other words…
extension Equatable where Self: Drinkable {} func == (lhs: Drinkable, rhs: Drinkable) -> Bool { return lhs.name == rhs.name && lhs.alcohol == rhs.alcohol }
Ta-dahhhhh!
Here is a playground, just for you: Protocol_Equatable.playground
Hi Cesar !
I’m in no way a Swift expert, but either there is a huge thing I can’t even see or the extension actually does not add anything.
With the extension commented out the Playground’s results are identical.
Moreover, Beer still does not conform to Equatable.
‘func == (lhs: Drinkable, rhs: Drinkable)’ just provides the ability to ‘compare’ (with == operator) two Drinkable but it does not make the Drinkable-conforming types Equatable.
func takesAnEquatable(param: T) {
print(param)
}
takesAnEquatable(guiness) // error: cannot invoke ‘takesAnEquatable’ with an argument list of type ‘(Beer)’
As opposed to a type that does conform to Equatable:
struct Water: Drinkable, Equatable {
let name: String
let alcohol: Float
}
func == (lhs: Water, rhs: Water) -> Bool { // Without that Water does not conform to Equatable
return lhs.name == rhs.name && lhs.alcohol == rhs.alcohol
}
takesAnEquatable(evian) // prints: Water(name: “Evian”, alcohol: 0.0)
‘func == (lhs: Drinkable, rhs: Drinkable)’ just provides the ability to ‘compare’ two Drinkable, even with different types, regardless their Equatable-conforming.
let evian = Water(name: “Evian”, alcohol: 0)
print(evian == guiness) // false
Equatable is about comparing two instances of the same type: func ==(_ lhs: Self, _ rhs: Self) -> Bool
Dude! This is amazing! Worked like a charm for me on Swift 3.0.
LOL – Dark Mode and I am getting black text on black background – so excuse any spellings!
iOS13 and Swift 5.1 – it still works! Fantastic!
Oops, sorry about that. Thanks for letting me know!
It should be fixed now