kelan.io

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!

Thanks to @jtbandes and NSHipster for help with this.