Implementing Equatable for Protocols in Swift
Last week I attended iOSDevCampDC, where I had the pleasure of hearing @ayanonagon‘s talk on testing. You can view the code sample here.
For testing purposes and to my surprise, she implemented a default Equatable on a protocol similar to this:
1 2 3 4 5 6 7 8 9 10 11 12 |
protocol Rectangle: Equatable { var width: Double { get } var height: Double { get} } // all objects that conform to this protocol // will now have default equality based on the // protocol properties func ==<T: Rectangle>(lhs: T, rhs: T) -> Bool { return lhs.width == rhs.width && rhs.height == lhs.height } |
I say to my surprise, because I never thought of having all objects that conform to a protocol have a default implementation like this. It definitely made me think! And of course it made sense for Ayaka’s example at the time – she added this purely for testing purposes.
But when I played around with this and thought about it some more, I started seeing future bugs. What if the value that conforms to this protocol has other properties as well. Now it has default equality that might not work as intended. For example:
1 2 3 4 5 6 7 8 9 10 11 12 |
struct ColorfulRectangle: Rectangle { let width: Double let height: Double let color: UIColor } let blueRectangle = ColorfulRectangle(width: 10, height: 10, color: .blueColor()) let redRectangle = ColorfulRectangle(width: 10, height: 10, color: .redColor()) // these two rectangles are the same, // despite having a different color blueRectangle == redRectangle // true |
Of course this would be fixed if ColorfulRectangle
got it’s own custom Equatable implementation:
1 2 3 4 5 6 7 8 |
func ==(lhs: ColorfulRectangle, rhs: ColorfulRectangle) -> Bool { return lhs.width == rhs.width && rhs.height == lhs.height && rhs.color == lhs.color } // false as expected blueRectangle == redRectangle // false |
But the worry is that this could be easily forgotten, especially since the compiler wouldn’t complain about the equality not being implemented.
So while implementing default Equality on a Protocol level is pretty powerful, it’s also a little dangerous. Use responsibly!
Join me for a Swift Community Celebration 🎉 in New York City on September 1st and 2nd. Use code NATASHATHEROBOT to get $100 off!