Search code examples
rustborrowing

How to check if two reference variable are borrowing the same object?


I have a struct that all store readonly references, for example:

struct Pt { x : f32, y : f32, }
struct Tr<'a> { a : &'a Pt }

I want to impl Eq for Tr to test if the underlaying a reference to exactly the same Pt :

let trBase1 = Pt::new(0.0, 0.0);
let trBase2 = Pt::new(0.0, 0.0);
assert!(trBase1 == trBase2);        // ok.
let tr1 = Tr::new(&trBase1);
let tr2 = Tr::new(&trBase2);
let tr3 = Tr::new(&trBase1);
assert!(tr1 == tr3);                // ok.
assert!(tr1.a == te2.a);            // ok. Using Eq for Pt that compare values.
assert!(tr1 != tr2);                // panicked! Not intended.

so now I have

impl<'a> PartialEq for Tr<'a> {
    fn eq(&self, v : &Tr<'a>) -> bool {
        // self.a == v.a // doesn't work.
    }
}

what should I write?


Solution

  • You can use std::ptr::eq to compare the addresses of two pointers. References (&T or &mut T) will automatically coerce to the underlying pointer (*const T) if fed to this function. Of course, it makes no sense for a mutable reference to have the same address as another reference, since mutable references are always exclusive references, but it can still be coerced to a *const T.

    // This derive will use the equality of the underlying fields
    #[derive(PartialEq)]
    struct Pt {
        x: f32,
        y: f32,
    }
    
    impl Pt {
        fn new(x: f32, y: f32) -> Self {
            Self { x, y }
        }
    }
    
    struct Tr<'a> {
        a: &'a Pt,
    }
    
    impl<'a> Tr<'a> {
        fn new(a: &'a Pt) -> Self {
            Self { a }
        }
    }
    
    // Here we use std::ptr::eq to compare the *addresses* of `self.a` and `other.a`
    impl<'a> PartialEq for Tr<'a> {
        fn eq(&self, other: &Tr<'a>) -> bool {
            std::ptr::eq(self.a, other.a)
        }
    }
    
    fn main() {
        let tr_base1 = Pt::new(0.0, 0.0);
        let tr_base2 = Pt::new(0.0, 0.0);
        assert!(tr_base1 == tr_base2);
    
        let tr1 = Tr::new(&tr_base1);
        let tr2 = Tr::new(&tr_base2);
        let tr3 = Tr::new(&tr_base1);
    
        assert!(tr1 == tr3);
        assert!(tr1.a == tr2.a);
        assert!(tr1 != tr2);
    }
    

    (playground link)