Why comparison operator (==) can't compare two references of unequal depth of reference(s)?

When we compare two references using the comparison operator (==), it(operator) follows all the references and performs the comparison on their final targets or values of the variables. In this case both references on the left and right hand side of the operator == must be of the exactly same depth. If depth is unequal then it shows the error.

Here is my code:

#![allow(unused)]

fn main()
{
    let a = 10;
    let b = 10;

    let ra = &a;
    let rb = &b;
    

    let rra = &ra;
    let rrb = &rb;

    assert!(rrb == rra); // works
    assert!(ra == rra); // Error; 
}

It seems to me that the comparison operator == compare two things: the type of the reference and the final value of the references they point to. In the last assert! macro, the comparison operator finds two different types {integer} and &{integer} so the compiler doesn't let it compile. I understand Rust's emphasis on type safety but I think in this case it could have been a little flexible because the type of the final value is exactly the same. Rust could have ignored the depth of the reference, and only cared about the type of the final value and the value itself.

What is the advantage of being strict when the comparison operator finds an unequal depth of references?

Are there any methods in any Rust crate to compare just the type of the final value and the value itself of two references disregarding how many dereferencing it takes to reach the final value?

Thanks.

It isn't designed like this because it's better. It's designed like this because the required impl blocks to get it to work with unequal depths would be overlapping, which rust does not allow. So it's a technical limitation.

7 Likes

To better understand this: there is no “comparison” of the type of the reference. == knows almost nothing about references. The code a == b is equivalent to PartialEq::eq(&a, &b). All of the compile-time results depend on the trait lookup that follows.

The reason that you can compare “through” references at all is because there is a trait implementation

impl<A, B> PartialEq<&B> for &A
where
    A: PartialEq<B> + ?Sized,
    B: ?Sized, 

That is: you can compare any two reference types &A and &B if you can compare A and B. This implementation effectively looks into one level of de-referencing on both sides of the comparison. Note also that both A and B could be reference types themselves. Eventually it will reach the impl PartialEq<i32> for i32 and perform an actual numeric comparison.

Now, exactly which PartialEq implementations can exist, and which implementations should exist, are more complex questions. The standard library could (I think) provide an implementation for comparison between i32 and &i32, thus allowing one level of number-of-references mismatch for integers. But, adding all of those explicit impls for all types would be a lot of code for small benefit, and @alice is saying that adding a generic impl for all comparisons between A and &B (and &A and B) would create overlap, so it's not allowed.

I hope this message clarified how comparison works. There are a lot of traits in Rust which have the same pattern of “acting on a reference forwards to the referent”; comparison is just a trickier one because there is both a left-side type and a right-side type, rather than only one type involved.

6 Likes