Traits to impl to mimic `Rc`?

Here is a struct I commonly use:

use super::*;
use std::{hash::Hasher, ptr, rc::Rc};

pub struct HashRcByLoc<T>(pub Rc<T>);

impl<T> std::hash::Hash for HashRcByLoc<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let t = &self.0;
        ptr::hash(&**t, state)
    }
}

impl<T> PartialEq for HashRcByLoc<T> {
    fn eq(&self, other: &Self) -> bool {
        Rc::ptr_eq(&self.0, &other.0)
    }
}

The point here is to do Eq/Hash by add of Rc rather than by contents of Rc.

One thing I don't like is that I often have to use ".0" to access the undelying Rc. What traits do I need to implement on HashRcByLoc to avoid the .0 ?

If you implement the Deref trait, for the most part, your &T can act like and be used in the place of &Target.

This is how Box acts is usable like the T it wraps, alongside the other "smart pointers" such as Arc and Rc.

Technically, you should probably have HashRcByLoc<T>: Deref<Target=T> rather than HashRcByLoc<T>: Deref<Target=Rc<T>>, due to the specific semantics of Deref, but any place one type should be used as-if it were another type is a reasonable enough place to impl Deref.

Also consider using shrinkwraprs to automate some of the newtype-forwarding boilerplate.

1 Like

Added Deref. Here's what I have so far:

pub struct RcByLoc<T: ?Sized>(pub Rc<T>);

impl<T> RcByLoc<T> {
    pub fn new(t: T) -> RcByLoc<T> {
        RcByLoc(Rc::new(t))
    }
}

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

impl<T: ?Sized> std::hash::Hash for RcByLoc<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let t = &self.0;
        ptr::hash(&**t, state)
    }
}

impl<T: ?Sized> PartialEq for RcByLoc<T> {
    fn eq(&self, other: &Self) -> bool {
        Rc::ptr_eq(&self.0, &other.0)
    }
}

impl<T: ?Sized> AsRef<T> for RcByLoc<T> {
    fn as_ref(&self) -> &T {
        &self.0.as_ref()
    }
}

impl<T: ?Sized> Deref for RcByLoc<T> {
    type Target = T;

    #[inline(always)]
    fn deref(&self) -> &Self::Target {
        &self.0.deref()
    }
}

suggestions?

You might be interested in my by_address crate. ByAddress<Rc<T>> implements Eq, PartialEq, Hash, Ord, and PartialOrd by address comparison. You can also use it with other shared pointer types, like ByAddress<Arc<T>> or ByAddress<&T>.

2 Likes