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

2

1

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 ?

Orang answered 1/11, 2017 at 16:18 Comment(2)
"inheriting Equatable" you mean conforming to Equatable...Twilatwilight
Agree but it doesn't tell me why it is different. Why I can compare optional with non-optional with Equatable and why I can compare only non optional when not inheriting EquatableOrang
D
1

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.

Dhu answered 1/11, 2017 at 16:31 Comment(5)
That's clear and now I know I can compare safely 2 optionals implementing Equatable.Orang
I guess it wraps the non-optional, then passes it through public func ==<T>(lhs: T?, rhs: T?) and then unwraps them both and uses the static function written by the dev. right?Twilatwilight
@Honey: Here github.com/apple/swift/blob/master/stdlib/public/core/… is the implementation. If both operands are non-nil (tested with the "optional pattern") then the unwrapped values are compared with ==.Dhu
Nice. So if at this line: case let (l?, r?): l is nil, then that line would just fail silently and it would move unto the default case?Twilatwilight
@Honey: case let (l?, r?) matches if both lhs and rhs are non-nil, and then binds l, r to the unwrapped values.Dhu
T
1

If you command click on the one that conforms to Equatable it would take you to here:

/// ....
/// You can also use this OPERATOR TO COMPARE A NON-OPTIONAL VALUE TO AN
/// OPTIONAL that wraps the same type. The non-optional value is wrapped as an
/// optional before the comparison is made. In the following example, the
/// `numberToMatch` constant is wrapped as an optional before comparing to the
/// optional `numberFromString`:
///
///     let numberToFind: Int = 23
///     let numberFromString: Int? = Int("23")      // Optional(23)
///     if numberToFind == numberFromString {
///         print("It's a match!")
///     }
///     // Prints "It's a match!"
///
/// ....
public func ==<T>(lhs: T?, rhs: T?) -> Bool where T : Equatable

But for the version that doesn't conform to Equatable, you don't get this. It will only use the static function you provided.

Twilatwilight answered 1/11, 2017 at 16:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.