1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
#![warn(missing_docs)]

//! A small crate to enable equality checks through pointer comparison

use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem::size_of_val;

/// Newtype for an immutable reference where [equality](Eq) is defined through
/// [pointer equality](std::ptr::eq)
///
/// Two `RefId`s are equal if the inner reference points to the same memory or
/// if the memory range pointed to has a size of zero.
pub struct RefId<'a, T: ?Sized>(
    /// inner value (immutable reference)
    pub &'a T,
);

impl<'a, T: ?Sized> Clone for RefId<'a, T> {
    fn clone(&self) -> Self {
        RefId(self.0)
    }
}

impl<'a, T: ?Sized> Copy for RefId<'a, T> {}

impl<'a, T: ?Sized> PartialEq for RefId<'a, T> {
    fn eq(&self, other: &Self) -> bool {
        (size_of_val(self.0) == 0 && size_of_val(other.0) == 0)
            || (self.0 as *const T == other.0 as *const T)
    }
}

impl<'a, T: ?Sized> Eq for RefId<'a, T> {}

impl<'a, T: ?Sized> Hash for RefId<'a, T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        if size_of_val(self.0) != 0 {
            (self.0 as *const T).hash(state)
        }
    }
}

impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for RefId<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}@{:?}", self.0, self.0 as *const _)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_two_refs_to_same_value() {
        let v = 1;
        let r1 = &v;
        let r2 = &v;
        assert_eq!(RefId(r1), RefId(r2));
    }
    #[test]
    fn test_two_boxes() {
        #[derive(Debug)]
        struct S {
            _value: i32,
        }
        let v1 = Box::new(S { _value: 1 });
        let v2 = Box::new(S { _value: 2 });
        let r1: &S = &v1;
        let r2: &S = &v2;
        assert_eq!(RefId(r1), RefId(r1));
        assert_ne!(RefId(r1), RefId(r2));
    }
    #[test]
    fn test_unit_type() {
        let v1 = ();
        let v2 = ();
        let r1: &() = &v1;
        let r2: &() = &v2;
        assert_eq!(RefId(r1), RefId(r2));
    }
    #[test]
    fn test_empty_slice() {
        let v = vec![5, 6, 7];
        let r1: &[i32] = &v[0..0];
        let r2: &[i32] = &v[1..1];
        assert_eq!(RefId(r1), RefId(r2));
    }
    #[test]
    fn test_nonempty_slices() {
        let v1 = vec![5, 6, 7];
        let v2 = vec![5, 6, 7];
        let r1: &[i32] = &v1[0..1];
        let r2: &[i32] = &v1[0..1];
        let r3: &[i32] = &v1[0..2];
        let r4: &[i32] = &v2[0..1];
        assert_eq!(RefId(r1), RefId(r1));
        assert_eq!(RefId(r1), RefId(r2));
        assert_ne!(RefId(r1), RefId(r3));
        assert_ne!(RefId(r1), RefId(r4));
    }
}