Swift - Equatable Structs
I was working with some structs, and wanted to compare them (so I could use them
with XCTAssertEqual()
). Here’s something similar, from a Playground:
struct S {
let i: Int
let str: String
}
let a = S(i: 0, str: "zero")
let b = S(i: 1, str: "one")
print(a == b) // <-- error: Binary operator '==' cannot be applied to two S operands
In retrospect, it should have been obvious why it wasn’t working, because the
definition of XCTAssertEqual()
says it works on <T: Equatable>
. (Although,
the error message of Cannot find an overload for 'XCTAssertEqual' that accepts
an argument list of type '(S, S)'
wasn’t terribly helpful.)
So, let’s make the struct adopt the Equatable
protocol. Cmd-clicking to the
definition of Equatable
shows:
protocol Equatable {
func ==(lhs: Self, rhs: Self) -> Bool
}
But you can’t simply put ==
as a method (as I first tried to do).
// This doesn't work:
extension S: Equatable {
func ==(lhs: S, rhs: S) -> Bool { // <-- error: Operators are only allowed at global scope
// ...
}
}
So, you need to make a free function that satisfies S: Equatable
:
extension S: Equatable {}
func ==(lhs: S, rhs: S) -> Bool {
return lhs.i == rhs.i && lhs.str == rhs.str
}
It’s a little odd that you have to have a free function to satisfy the protocol, but apparently that’s how it works.
So, we end up with:
struct S {
let i: Int
let str: String
}
extension S: Equatable {}
func ==(lhs: S, rhs: S) -> Bool {
return lhs.i == rhs.i && lhs.str == rhs.str
}
let a = S(i: 0, str: "zero")
let b = S(i: 1, str: "one")
let c = S(i: 0, str: "zero")
print(a == b) // <-- false
print(a == c) // <-- true
Arrays of Structs
As a bonus, once you have your struct be Equatable
, then you can also compare arrays and
dictionaries of them.
let arr1 = [ a, b ]
let arr2 = [ c, b ]
print(arr1 == arr2) // <-- true
let dict1 = [ "a": a, "b": b ]
let dict2 = [ "a": c, "b": b ]
print(dict1 == dict2) // <-- true
Neat!