Reputation: 1498
Can anybody explain please, what is going on here:
struct Test {
var value: Int
}
// -----------------------------------
protocol Test1: Equatable {
var value: Int { get set }
}
extension Test1 {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.value == rhs.value + 1
}
}
// -----------------------------------
protocol Test2: Equatable {
var value: Int { get set }
}
extension Test2 {
static func == (lhs: Self, rhs: Self) -> Bool {
lhs.value == rhs.value + 2
}
}
// -----------------------------------
extension Test: Test1 {}
extension Test: Test2 {}
let a = Test(value: 5)
let b = Test(value: 5)
print(a == b) // true, which is unexpected
If conforms only to Test1
or only to Test2
it works as expected.
Conforming to Test1
and Test2
. At first I thought that the order will matter. But looks like it just cancels each other! Without any warnings. Which is very counterintuitive.
Upvotes: 3
Views: 87
Reputation: 271735
Note: Test
conforms to Equatable
not because of the two extensions you provided, but because of the auto-generated Equatable
implementation. As you said, if there were only those two extensions, it would be ambiguous which ==
is the Equatable
implementation.
Neither of the ==
in the Test1
or Test2
protocol extensions are called. Instead, the auto-generated Equatable
implementation is called. As you may recall, a ==
operator is auto-generated for types whose properties are all Equatable
, and is declared to conform to Equatable
itself.
This is because members declared in extensions use static binding, so when there are multiple versions of the same member available, the version declared in the extension will only be chosen if the compile time type is the extension's type. For example:
protocol P { }
class C : P { func f() { print("C") } }
extension P { func f() { print("P") } }
C().f() // C
(C() as P).f() // P
In a == b
, both a
and b
are Test
, so none of the extension operators gets chosen. However, since Test1
and Test2
both use Self
, you can only use them as generic constraints, and can't cast to them either. Therefore, I don't think you will be able to call the ==
declared in the extensions at all.
Anyway, if you want to see an error message saying that there are duplicate ==
operators available:
struct Test {
var value: Int
var x: Any? // now there is no auto-generated Equatable implementation!
}
error: type 'Test' does not conform to protocol 'Equatable' extension Test: Test1 {} ^
note: candidate exactly matches
static func == (lhs: Self, rhs: Self) -> Bool { ^
note: candidate exactly matches
static func == (lhs: Self, rhs: Self) -> Bool { ^
If you remove one of the extensions, then Test
conforms to Equatable
because of the extension, as there is no more ambiguity. Therefore, the auto-generated implementation isn't auto-generated anymore, and there is only one ==
to choose from - the one declared in the extension.
Upvotes: 3