Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous use of '!=' operator

So, I'm trying to gat CGPoint, CGVector, and CGSize to work nicely for me when using SpriteKit. All of them are just structs with a vertical and horizontal component (vectors). So I made a protocol: VectorType

protocol VectorType {
    init(x: CGFloat, y: CGFloat)

    var x: CGFloat { get set }
    var y: CGFloat { get set }
}

Of course I extended the 3 structs to conform to the protocol and connect the xand y to the horizontal and vertical components of each struct i.e. x returns dx for CGVector (same for setting), x returns width for CGSize , and nothing for CGPoint as they conform to it out of the box, just left the extension empty.

Now I overloaded the "main" operators (+ - * /...) so I can effortlessly perform operations involving different types of structs without having to cast them or create new objects, but the main thing here is that I also overloaded the equivalence operators like this:

//Compiler requires me to use generics for some reason
func == <T: VectorType, U: VectorType> (lhs: T, rhs: U) -> Bool {
    return (lhs.x == rhs.x) && (lhs.y == rhs.y)
}

func != <T: VectorType, U: VectorType> (lhs: T, rhs: U) -> Bool {
    return !(lhs == rhs)
}

Now, when I test this code everything is fine except for the != operator. For testing these operators I compare sizes to sizes, sizes to vectors, and sizes to points and so on for each type. When I use == there is no problem.

Equal operator test

but then, when I use != there is a problem. There is an Ambiguous use of operator '!='like this:

Not equal operator test

I totally get where this is coming from: overloads for the == and != operators comparing CGVector to CGVector, CGPoint to CGPoint, and CGSize to CGSize already exist. They are declared like this

@warn_unused_result func ==(lhs: CGSize, rhs: CGSize) -> Bool

There is an overload for every type of course. So I get where the ambiguity comes from, it doesn't know which operator to use when comparing types that are the same. But I don't understand why there is no such problem with the == operator in testEqual() if we basically have the same situation.

It seems like a compiler bug to me, but I'm not sure, I tried cleaning the project, restarting Xcode, and creating a new project, but to still does not work. Additionally when I try to see the other declaration that causes the ambiguity selecting Found this candidate it just shows nothing.

So, the question is: How can I make it work, or can you suggest another way to make it work (that doesn't involve creating a different operator)?

Update

I found out that actually the == and != implementations being used are actually declared differently. This is how the == overload being used is declared

@warn_unused_result func ==(lhs: CGSize, rhs: CGSize) -> Bool

AFAIK this should also conflict with my declaration, but it apparently does not.

And here is the other != overload declaration.

@warn_unused_result func !=<T : Equatable>(lhs: T, rhs: T) -> Bool

Since lhs and rhs have the same type and all three types conforming to the VectorType protocol conform to Equatable this overload is a candidate for that operation.

I guess the == gets used because it explicitly asks for a CGVector, CGPoint or CGSize, and perhaps that takes a precedence over generics. Not sure let me know if you know why both == operators do not conflict.

like image 686
lsauceda Avatar asked Oct 22 '25 03:10

lsauceda


1 Answers

If your type conforms to Equatable you are required to define the equality operator == for it, but then the standard library provides the inequality operator != for you in terms of your equality operator.

(In fact, if you look at the Equatable protocol requirements the only required function is func ==)

Your ambiguity error is because there are two definitions: your own and the one provided by the compiler. They both have the same signature, so the compiler can't resolve it.

Just don't define func != yourself - use the provided one.

If you find that you NEED to define it, you're probably not really working with an Equatable type.

Update

You're not working with equatable types. Asking if a point is equal to a size is meaningless. Your issues stem from trying to force that to have meaning.

You are providing an Equality operator for every combination of types. So, you are providing CGPoint()==CGVector(), CGPoint()!=CGVector(), CGPoint()=CGSize() and CGPoint()!=CGSize(), but you're also providing CGPoint()==CGPoint() and CGPoint()!=CGPoint(), which clash with the ones in CoreGraphics. read on to understand why you get the error messages that you do, and why == seems to work.

Equality:

CGPoint, CGVector and CGSize all conform to Equatable, and provide an equality operator where both sides are the same type.

You have then provided an equality operator that works on these types via your extension:

func == <T: VectorType, U: VectorType> (lhs: T, rhs: U) -> Bool

This declares an equality operator that works on two VectorType objects, even if they're of different underlying types.

The compiler searches for a direct match first, then if it doesn't find it it tries to generate one by substituting types in a generic one.

So, when you say CGPoint()==CGPoint() it will look for func ==(lhs:CGPoint, rhs:CGPoint)->Bool which it finds in CoreGraphics.

When you say CGPoint()==CGVector() it will look for func ==(lhs:CGPoint, rhs:CGVector)->Bool. This isn't defined in CoreGraphics or anywhere else, so it moves on to building one from a generic definition.

You have provided:

func == <T: VectorType, U: VectorType> (lhs: T, rhs: U) -> Bool

via your protocol and extensions, so it takes your definition, substitutes T with CGPoint and U with CGVector to produce:

func == (lhs:CGPoint, rhs:CGPoint) -> Bool

It's found one (and only one) useable definition, so it uses it.

Inequality:

When you say CGPoint()!=CGPoint() it will look for func !=(lhs:CGPoint, rhs:CGPoint)->Bool which isn't defined. It moves to to build one from the generic definitions. It finds

func !=(lhs:T, rhs:T)->Bool

in the standard library, substitutes T with CGPoint to produce: func !=(lhs:CGPoint, rhs:CGPoint)->Bool

which satisfies the requirements.

However, since you've declared:

func != <T: VectorType, U: VectorType> (lhs: T, rhs: U) -> Bool

(either directly, or via Equatable)

it can substitute both T and U with CGPoint to get:

func !=(lhs:CGPoint, rhs:CGPoint)->Bool

Now it has two ways to satisfy the requirements. There is no way to decide which definition it should use, so it stops with the ambiguity error.

like image 165
DavidA Avatar answered Oct 24 '25 20:10

DavidA



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!