迭代获取子节点的可变引用

Iteratively get mutable references of child nodes

我需要一个类似于assocPath的功能,我想确认某个子节点是否存在,如果存在,则获取其可变引用,如果不存在,则创建并获取。

我遇到了一些困难,我的父节点是一个可变引用,子节点也是一个可变引用,所以应该释放父节点的引用,但是编译器怎么告诉我“无法赋值”两次不可变变量”,这让我很困惑。

以下是我的尝试,请问如何解决?

use std::collections::HashMap;

pub enum JsonValue {
    Null,
    Bool(bool),
    Number(f32),
    String(String),
    Array(Vec<JsonValue>),
    Object(HashMap<String, JsonValue>),
}

impl JsonValue {
    fn new() -> JsonValue { JsonValue::Object(HashMap::new()) }
    fn check_key_path(mut self, path: &str) -> &JsonValue {
        let ref mut node = self;
        for k in path.split(".") {
            match node {
                JsonValue::Object(dict) => {
                    node = match dict.get(k) {
                        Some(ref mut s) => s,
                        None => &mut dict.insert(k.to_string(), JsonValue::new()).unwrap()
                    }
                }
                _ => panic!("NotObject")
            }
        }
        node
    }
}

fn main() {
    let mut v = JsonValue::new();
    v.check_key_path("some.node.path");
}

这里有几个问题:

  • check_key_path 取值 self,这意味着 self 被该方法消耗。一旦 self 被消耗,您想要从方法中 return 的 &JsonValue 将被删除,因此引用无效。
  • let ref mut 声明一个 immutable 绑定到一个可变的 reference,这意味着您可以更改引用的数据但不能更改什么数据具有约束力的参考资料。这是您在尝试重新分配 node.
  • 时看到的错误
  • 你在调用 dict.get(k) 时借用 dict 作为不可变的,然后尝试 dict.insert()(可变地借用 dict)。

标准库提供了一种类型 Entry<K, V> 来帮助处理这类事情:您可以从 HashMap 中获取一个值,或者如果它不存在则创建它 Entry::or_insert.

这是更新代码的 playground link,它看起来像这样:

// takes &mut self instead of mut self
fn check_key_path(&mut self, path: &str) -> &JsonValue {
    // declare node as a mutable binding with value &mut self
    let mut node = self;
    for k in path.split(".") {
        match node {
            JsonValue::Object(dict) => {
                // get or create value with key k
                node = dict.entry(k.to_string()).or_insert(JsonValue::new());
            }
            _ => panic!("NotObject")
        }
    }
    node
}