Search code examples
swiftoperator-overloadingoption-typeswift-protocolsequatable

why swift compiler behaves differently with equality operator with/without Equatable protocol


I have a very simple class in a Playground in Swift 4.0 that overrides the == operator.

I'm not understanding why the Swift complier doesn't behave the same when the class inherits/doesn't inherit Equatable protocol.

Here the class when inheriting Equatable protocol

class Test: Equatable  {
    var value = 0

    init(_ initialValue:Int) {
        value = initialValue
    }

    static func == (lhs:Test, rhs:Test) -> Bool {
        return lhs.value == rhs.value ? true : false
    }
}

let test1 = Test(0)
var test4:Test? = nil

if test1 == test4 {
    print("test1 and test4 are equals")
} else {
    print("test1 not equals to test4")
}

When this code executes it displays "test1 not equals to test4". It's the expected behavior.

Next, when I just remove the "Equatable" protocol from the class

class Test  {
    var value = 0

    init(_ initialValue:Int) {
        value = initialValue
    }

    static func == (lhs:Test, rhs:Test) -> Bool {
        return lhs.value == rhs.value ? true : false
    }
}

let test1 = Test(0)
let test3 = Test(0)

var test4:Test? = nil


if test1 == test4 {
    print("test1 and test4 are equals")
} else {
    print("test1 not equals to test4")
}

I get a compilation error on the line

if test1 == test4 {

with the following message: "Value of optional type 'Test?' not unwrapped; did you mean to use "!" or '?'?

Why the behavior is different with/without Equatable?

In fact, I was also expecting the same compilation error when the class inherits from Equatable because I compare a non-optional with an optional.

Is it safe to compare a non-optional with an optional when a class inherits Equatable ?


Solution

  • There is a == operator

    public func ==<T>(lhs: T?, rhs: T?) -> Bool where T : Equatable
    

    which allows to compare two optional values if the underlying type is Equatable. That operator is called in your first case

    let test1 = Test(0)
    var test4:Test? = nil
    
    if test1 == test4 { ... }
    

    (and the left operand is automatically wrapped into an optional.)

    If Test does not conform to Equatable then that operator does not match, so that there is no == operator taking two Test? operands. Therefore the compiler error.