Clone HashMap<Weak, Weak> as HashMap<Rc, Rc>

It seems like Rc wrapping RefCell isn't implementing Hash. I'm trying to clone a HashMap<Weak, Weak> as a HashMap<Rc, Rc>:

type RcCell<T> = Rc<RefCell<T>>;
type WeakCell<T> = Weak<RefCell<T>>;

type SymbolRef = RcCell<Symbol>;
type WeakSymbolRef = WeakCell<Symbol>;

struct S {
    _some_list: Option<HashMap<WeakSymbolRef, WeakSymbolRef>>,
}

impl Symbol for RcCell<S> {
    fn some_list(&self) -> HashMap<SymbolRef, SymbolRef> {
        if let Some(ls) = self.borrow()._some_list {
            ls.iter().map(|(k, v)| (k.upgrade().unwrap(), v.upgrade().unwrap())).collect()
        } else {
            HashMap::new()
        }
    }
}

collect() fails with:

the trait bound std::cell::RefCell<(dyn symbols::Symbol + 'static)>: std::hash::Hash is not satisfied

the trait std::hash::Hash is not implemented for std::cell::RefCell<(dyn symbols::Symbol + 'static)>

I'm expecting for Rc to use its internal pointer for hashing in this case. Is there an alternative to iter().map().collect()? Or is there an alternative for HashMap?

Based on another topic, I tried to implement Hash for SymbolRef:

use std::hash::{Hash, Hasher};

impl Hash for SymbolRef {
    fn hash<H: Hasher>(&self, state: &mut H) {
        std::ptr::hash(&**self, state)
    }
}

But Rustc complains:

conflicting implementations of trait std::hash::Hash for type std::rc::Rc<std::cell::RefCell<(dyn symbols::Symbol + 'static)>>

It doesn't. Smart pointers and references defer equality, comparison/ordering, and related traits to the wrapped type. If you need to deviate from this convention, use newtype wrappers, not typedefs. (Typedefs are structural, type A = B declares A to be an alias for B, it does not create a new type.)

I'm trying this:

use std::cell::{RefCell};
use std::rc::{Rc, Weak};
use std::hash::{Hash, Hasher};

pub type WeakCell<T> = std::rc::Weak<std::cell::RefCell<T>>;

#[derive(Clone)]
pub struct RcCell<T> {
    _rc: Rc<RefCell<T>>,
}

impl<T> RcCell<T> {
    fn new(v: Rc<RefCell<T>>) -> Self {
        Self {
            _rc: v.clone()
        }
    }
}

impl<T> std::ops::Deref for RcCell<T> {
    type Target = Rc<RefCell<T>>;
    fn deref(&self) -> &Self::Target {
        &self._rc
    }
}

impl<T> Hash for RcCell<T> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        std::ptr::hash(Rc::as_ptr(&self._rc), state)
    }
}

It builds, but it breaks my code. I did try a crate named rccell too, but it didn't accept RcCell<dyn Symbol>.

You probably want:

pub struct MyRc<T>(pub Rc<T>)

then you can define impl Hash / Eq on MyRc

Yes, because you defined Hash but not PartialEq and Eq using pointer identity. (Thus, any equality comparison probably went through the Deref impl, so you were back at square 1.)

2 Likes

I relied little to nothing on reference equality. I've just implemented PartialEq and Clone anyway and derived Eq and nothing changed. (Also I'm implementing Clone manually because if I don't, deriving Clone will return Rc, not RcCell itself.)

impl<T> Clone for RcCell<T> {
    fn clone(&self) -> Self {
        Self { _rc: self._rc.clone() }
    }
}

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

To be more specific, I'm getting tons of this same error:

the size for values of type (dyn symbols::Symbol + 'static) cannot be known at compilation time

doesn't have a size known at compile-time

help: the trait std::marker::Sized is not implemented for (dyn symbols::Symbol + 'static)

Your struct definition and all the relevant impl blocks need T: ?Sized to support trait objects.

pub struct MyRc<T: ?Sized>(pub Rc<T>)

impl<T: ?Sized> PartialEq for MyRc<T> ...
2 Likes

That cleared up the issues, but somehow collect() still fails with:

the trait bound (dyn symbols::Symbol + 'static): std::cmp::Eq is not satisfied

Here's my RcCell and WeakCell implementation:

use std::cell::{RefCell};
use std::rc::{Rc, Weak};
use std::hash::{Hash, Hasher};

#[derive(Eq)]
pub struct RcCell<T: ?Sized> {
    _rc: Rc<RefCell<T>>,
}

impl<T: ?Sized> RcCell<T> {
    pub fn new(v: Rc<RefCell<T>>) -> Self {
        Self {
            _rc: v.clone()
        }
    }

    pub fn downgrade(&self) -> WeakCell<T> {
        WeakCell::new(Rc::downgrade(&self._rc))
    }
}

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

impl<T: ?Sized> std::ops::Deref for RcCell<T> {
    type Target = Rc<RefCell<T>>;
    fn deref(&self) -> &Self::Target {
        &self._rc
    }
}

impl<T: ?Sized> Hash for RcCell<T> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        std::ptr::hash(Rc::as_ptr(&self._rc), state)
    }
}

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

pub struct WeakCell<T: ?Sized> {
    _w: Weak<RefCell<T>>,
}

impl<T: ?Sized> WeakCell<T> {
    pub fn new(v: Weak<RefCell<T>>) -> Self {
        Self {
            _w: v.clone()
        }
    }

    pub fn upgrade(&self) -> Option<RcCell<T>> {
        if let Some(v) = self._w.upgrade() { Some(RcCell::new(v)) } else { None }
    }
}

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

impl<T: ?Sized> std::ops::Deref for WeakCell<T> {
    type Target = Weak<RefCell<T>>;
    fn deref(&self) -> &Self::Target {
        &self._w
    }
}

impl<T: ?Sized> Hash for WeakCell<T> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        std::ptr::hash(&self._w.into_raw(), state)
    }
}

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

Write a manual impl Eq rather than using derive.

The problem with derive is that it places bounds on the impl it generates, which are incorrect in many cases (including this one).

3 Likes

That resolved, thank you!

I'm not sure, but isn't that exactly what your by_address crate (or in a different but similar way my crate refid) does, but as a generic? See also: Working with identity (comparing equality of references/pointers).

Edit: I mean implementing all three traits PartialEq, Eq, and Hash on a newtype to define equality "by address".

1 Like

This seems useful, but it does not implement upgrade() and downgrade() at the wrapper-level. For example, Rc::downgrade(&*ByAddress(someRc)) returns Weak instead of ByAddress<Weak>.

Yeah, by_address is very similar, but it doesn't work with Weak because of how I implemented it using the Deref trait.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.