I have the structs Value and RefValue in my project. RefValue is a reference-counted, dynamically borrowable Value. Now Value may contain a HashMap of RefValue, where both the key and the value is a RefValue.
type ValueMap = HashMap<RefValue, RefValue>;
#[derive(Debug, PartialEq, Eq)]
enum Value {
    Integer(i64),
    String(String),
    Map(ValueMap),
}
#[derive(Debug, PartialEq, Eq)]
struct RefValue {
    value: Rc<RefCell<Value>>,
}
I've implemented Hash on RefValue on my own, and some From-traits separately in this playground.
What I want to achieve is something like this main program:
fn main() {
    // Simple values
    let x = RefValue::from(42);
    let y = RefValue::from("Hello");
    // Make a map from these values
    let mut z = ValueMap::new();
    z.insert(RefValue::from("x"), x);
    z.insert(RefValue::from("y"), y);
    // Make a value from the map
    let z = RefValue::from(z);
    println!("z = {:?}", z);
    // Try to access "x"
    if let Value::Map(m) = &*z.borrow() {
        println!("m[x] = {:?}", m["x"]);  // <- here I want to access by &str
    };
}
Unfortunately I'm getting strange results, as you can find in the playground comments. I'm also quite unsure if there's not a better implementation of the entire problem, as the RefCell cannot return a borrowed value of its contained element.
Can anybody give me a hint?
                        
When you implement
Borrow<T>, yourHashimplementation must return the same hash value asT's for when the underlying value is equivalent. That is, ifx.hash()must be equal tox.borrow().hash().HashMaprelies on this property when you index into it: it requiresIdx: Borrow<Key>and then uses this rule to ensure it can find the value.Your
impl Borrow<str> for RefValuedoes not follow this rule.RefValue::hash()forRefValue::Stringcallswrite_u8(2)before hashing the string. Since you broke the contract, the hashmap is allowed to do anything (excluding undefined behavior), like panicking, aborting the process, or not finding your key, which is what it does in this case.To fix that, you should just not hash the discriminant (removed it from the others too, for consistency):
Now it panics in your
Borrowimplementation, like you expected (playground).But you should not implement
Borrow, since implementing it means your value is a reflection of the borrowed value.RefValueis by no meansstr. It can be integers, or maps, too. Thus, you should not implementBorrowfor any of those. You could implementBorrow<Value>, but this is impossible because you useRefCelland thus need to returnRefbutBorrowmandates returning a reference. You're out of luck. Your only option is to index withRefValues.Lastly, you should avoid interior mutability in keys. Once change them, and it's easy to change them by mistake, and your hash/equality change, you broke your contract with the map once again.