借用循环内使用的可变成员

Borrowing the mutable member used inside the loop

我要解决的问题是:

Given the recursively nested data structure, eg. a JSON tree, and a path pointing to (possibly non-existent) element inside it, return the mutable reference of the element, that's the closest to given path.

示例:如果我们有 JSON 格式 { a: { b: { c: "foo" } } } 的文档和路径 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 语句内,一次在函数末尾,当构造指针方法时.

我尝试了不同的方法,但没有想出如何实现目标(可能走不安全的路)。

我完全误读了你的问题,我向你道歉。

你不能一次完成 - 你需要做一个只读的过程来找到最近的路径(或确切的路径),然后一个读写的过程来实际提取引用,或者以闭包的形式传递一个增变函数。

我已经 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() :-)