我要解决的问题是:
给出递归嵌套的数据结构,例如。一个JSON树,以及指向其中的元素(可能不存在)的路径,返回该元素的可变引用,该引用最接近给定路径。
示例:如果我们有格式为{ a: { b: { c: "foo" } } }
的JSON文档和路径为a.b.d
的路径,我们希望有一个可变的指向存储在键“ b”下的值的指针。
这是一个代码段,到目前为止,我已经了解了:
use std::collections::HashMap;
enum Json {
Number(i64),
Bool(bool),
String(String),
Array(Vec<Json>),
Object(HashMap<String, Json>)
}
struct Pointer<'a, 'b> {
value: &'a mut Json,
path: Vec<&'b str>,
position: usize
}
/// Return a mutable pointer to JSON element having shared
/// the nearest common path with provided JSON.
fn nearest_mut<'a,'b>(obj: &'a mut Json, path: Vec<&'b str>) -> Pointer<'a,'b> {
let mut i = 0;
let mut current = obj;
for &key in path.iter() {
match current {
Json::Array(array) => {
match key.parse::<usize>() {
Ok(index) => {
match array.get_mut(index) {
Some(inner) => current = inner,
None => break,
}
},
_ => break,
}
} ,
Json::Object(map) => {
match map.get_mut(key) {
Some(inner) => current = inner,
None => break
}
},
_ => break,
};
i += 1;
}
Pointer { path, position: i, value: current }
}
问题在于,这不会通过Rust的借位检查器传递,因为在构造指针方法时,两次将current
作为可变引用来借用一次,一次在match语句内,一次在函数末尾。 / p>
我尝试了不同的方法,但没有弄清楚如何实现目标(也许走不安全的道路)。
答案 0 :(得分:1)
我完全误解了您的问题,并向您道歉。
您不能一pass而就-您将需要执行只读操作以找到最近的路径(或精确路径),然后进行读写操作以实际提取参考或闭包形式的mutator函数。
我为您implemented进行了两次通过。请注意,它仍然表现出色:
fn nearest_mut<'a, 'b>(obj: &'a mut Json, path: Vec<&'b str>) -> Pointer<'a, 'b> {
let valid_path = nearest_path(obj, path);
exact_mut(obj, valid_path).unwrap()
}
fn exact_mut<'a, 'b>(obj: &'a mut Json, path: Vec<&'b str>) -> Option<Pointer<'a, 'b>> {
let mut i = 0;
let mut target = obj;
for token in path.iter() {
i += 1;
// borrow checker gets confused about `target` being mutably borrowed too many times because of the loop
// this once-per-loop binding makes the scope clearer and circumvents the error
let target_once = target;
let target_opt = match *target_once {
Json::Object(ref mut map) => map.get_mut(*token),
Json::Array(ref mut list) => match token.parse::<usize>() {
Ok(t) => list.get_mut(t),
Err(_) => None,
},
_ => None,
};
if let Some(t) = target_opt {
target = t;
} else {
return None;
}
}
Some(Pointer {
path,
position: i,
value: target,
})
}
/// Return a mutable pointer to JSON element having shared
/// the nearest common path with provided JSON.
fn nearest_path<'a, 'b>(obj: &'a Json, path: Vec<&'b str>) -> Vec<&'b str> {
let mut i = 0;
let mut target = obj;
let mut valid_paths = vec![];
for token in path.iter() {
// borrow checker gets confused about `target` being mutably borrowed too many times because of the loop
// this once-per-loop binding makes the scope clearer and circumvents the error
let target_opt = match *target {
Json::Object(ref map) => map.get(*token),
Json::Array(ref list) => match token.parse::<usize>() {
Ok(t) => list.get(t),
Err(_) => None,
},
_ => None,
};
if let Some(t) = target_opt {
target = t;
valid_paths.push(*token)
} else {
return valid_paths;
}
}
return valid_paths
}
原理很简单-为了获得最近的有效路径(或精确路径),我重用了我在最初的问题中写的方法。
从那里,我直接将其输入到原始答案中的函数中,并且由于我确定路径是有效的(来自先前的函数调用),因此我可以安全地unwrap()
:-)>