实施借用回避背后的东西?

发布于 2025-01-23 10:40:50 字数 1401 浏览 0 评论 0 原文

我的项目中有structs value repvalue repvalue 是一种参考计数,动态借出的 value 。现在, value 可能包含一个refvalue的哈希图,其中键和值都是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>>,
}

我已经独自在repvalue上实现了哈希,在此 Playground

我想实现的是这样的主要程序:

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
    };
}

不幸的是,我会在操场上的评论中找到奇怪的结果。我也不确定是否没有更好地实现整个问题,因为重弹性无法返回其包含元素的借来的值。

有人可以给我提示吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

逆蝶 2025-01-30 10:40:50

实现借用&lt; t&gt; hash 实现必须返回与 t s相同的哈希值,以便何时基础值等于。也就是说,如果 x.hash() 必须等于 x.borrow()。hash() Hashmap 索引到其中时依赖于此属性:它需要 idx:borrow&lt; key&gt; ,然后使用此规则来确保它可以找到值。

您的 impl borrow&lt; str&gt;对于repvalue 不遵循此规则。 Refvalue :: Hash() for refvalue :: String 调用 write_u8(2) hashing handing string。由于您违反了合同,因此允许hashmap做任何事情(不确定的行为),例如惊慌,流产或找不到钥匙,在这种情况下,这就是它的作用。

为了解决这个问题,您不应该放弃判别物(也将其从其他判别中删除,以保持一致性):

impl Hash for RefValue {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match &*self.borrow() {
            Value::Integer(i) => {
                i.hash(state);
            }
            Value::String(s) => {
                s.hash(state);
            }
            Value::Map(m) => {
                (m as *const ValueMap as usize).hash(state);  // Object address
            }
        }
    }
}

现在它在您的 borrow 实施中感到恐慌(

但是您不应实现借用,因为实现它意味着您的价值是借用价值的反映。 Refvalue 绝不是 str 。它也可以是整数或地图。因此,您不应为任何一个实现借用。您可以实现 borrow&lt; value&gt; ,但这是不可能的代码>命令返回参考。你不幸。您唯一的选择是用 refvalue s索引。

最后,您应该避免钥匙内部的内部可变性。一旦更改它们,很容易通过错误地更改它们,并且您的哈希/平等更改,您将再次与地图违反了合同。

When you implement Borrow<T>, your Hash implementation must return the same hash value as T's for when the underlying value is equivalent. That is, if x.hash() must be equal to x.borrow().hash(). HashMap relies on this property when you index into it: it requires Idx: Borrow<Key> and then uses this rule to ensure it can find the value.

Your impl Borrow<str> for RefValue does not follow this rule. RefValue::hash() for RefValue::String calls write_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):

impl Hash for RefValue {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match &*self.borrow() {
            Value::Integer(i) => {
                i.hash(state);
            }
            Value::String(s) => {
                s.hash(state);
            }
            Value::Map(m) => {
                (m as *const ValueMap as usize).hash(state);  // Object address
            }
        }
    }
}

Now it panics in your Borrow implementation, like you expected (playground).

But you should not implement Borrow, since implementing it means your value is a reflection of the borrowed value. RefValue is by no means str. It can be integers, or maps, too. Thus, you should not implement Borrow for any of those. You could implement Borrow<Value>, but this is impossible because you use RefCell and thus need to return Ref but Borrow mandates returning a reference. You're out of luck. Your only option is to index with RefValues.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文