引用删除后哈希映射多个可变借用问题

Hashmap multiple mutable borrow issue after reference drop

我试图传递一个 HashMap,它通过一组嵌套 enums/structs 存储值。迭代过程中出现多重可变性问题,甚至所有引用应该被删除。

总体思路是拥有一个值向量,遍历它们并简化它们,在 HashMap 中跟踪它们。有两个简化阶段。

一般流程类似于

run(Vec<ComplexVal>) 
-for each val-> 
val.fix_complex(holder) 
-for each `smp` SimpleVal in val->
basicval = Simplifier::step(smp, holder)
holder.insert("name", basicval)

但问题是 holder 在每个阶段都是可变借用的,并且没有 supposedComplexVal 的任何引用到 holder 并且由于 borrowchecker 不喜欢多次借用,它失败了。

完整的游乐场片段:here

它发生在这个片段中:

    pub fn run(&mut self, mut vals: Vec<ComplexVal>) {
        let mut holder = Holder{hold:HashMap::new()};
        // .. setup holder code omitted
        let len = vals.len();
        for _ in 0..len {
            let mut val = vals.remove(0); // remove from vec, should drop after running
            println!("Running {:?}", val);
            match val {
                ComplexVal::Cmplx1(mut c) => {
                    c.fix_complex(&mut holder)
                },
                //... more cases of different types of values omitted for simplicity
            }
            // val *should* be dropped here, and therefore the mutable borrow of holder?
        }

        println!("Holder: {:?}", holder);
    }
}

我唯一能想到的是它在某种程度上与创建时的BasicVal::Ref(&BasicVal)值有关。

我需要 return 类型 &BasicVal 的引用,所以我不能使用常规 fn() -> &BasicVal 因为引用会悬空,所以我传递了 ret 值被修改并用作 return 值的存储。

我也曾尝试 return 枚举 BasicVal::Ref(&BasicVal),但 运行 遇到了相同的可变性问题。

下面的例子是一个更简单的版本,它(在某种程度上)展示了同样的错误,只是想我会包括这个上下文,以防有人对如何实现这个有另一个想法而不会出现这些问题

代码(已编辑)

Updated playground link

编辑:我犯了一个错误,不需要 holderret 的生命周期明确地相同,所以我为它做了一个更新的例子

use std::borrow::BorrowMut;
///////////////////////////////
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;

#[derive(Debug)]
enum BasicVal<'a> {
    Ref(&'a BasicVal<'a>),
    Val1(BasicStruct),
}

#[derive(Debug)]
struct Holder<'b> {
    hold: HashMap<String, RefCell<BasicVal<'b>>>,
}

#[derive(Debug)]
struct BasicStruct {
    val: i32,
}

impl<'a> BasicVal<'a> {
    pub fn empty() -> Self { BasicVal::Val1(BasicStruct { val: 0 }) }
}

// must match sig of modify_val_ref
fn modify_val<'f>(holder: &'f mut Holder<'f>, mut ret: RefMut<BasicVal<'f>>) {
    *ret = BasicVal::Val1(BasicStruct { val: 5 });
}

// must match sig of modify_val
fn modify_val_ref<'f>(holder: &'f mut Holder<'f>, mut ret: RefMut<BasicVal<'f>>) {
    ret = holder.hold.get("reference_val").unwrap().borrow_mut();
}


fn do_modify<'f>(holder: &'f mut Holder<'f>) {
    let mut v = RefCell::new(BasicVal::empty());
    println!("Original {:?}", v);

    modify_val(holder, v.borrow_mut());
    holder.hold.insert("Data".to_string(), v);

    println!("Modified {:?}", holder.hold.get("Data"));
}

pub fn test_dropborrow() {
    let mut holder = Holder { hold: HashMap::new() };
    holder.hold.insert(
        "reference_val".to_string(),
        RefCell::new(BasicVal::Val1(BasicStruct { val: 8 })),
    );
    do_modify(&mut holder);
}

pub fn main() {
    test_dropborrow();
}

编辑:仅使用 holder 作为临时 return 值给我带来了多重可变借用问题,因此该解决方法不起作用。我也用同样问题的 RefCell 尝试过。

fn modify_val<'f>(holder: &'f mut Holder<'f>) {
    holder.hold.insert("$return".to_string(), BasicVal::Val1(BasicStruct{val: 5}));
}

fn do_modify<'f>(holder: &'f mut Holder<'f>) {

    modify_val(holder);
    let mut v = holder.hold.remove("$return").unwrap();
    holder.hold.insert("Data".to_string(), v);

    println!("Modified {:?}", v);
}

错误:

935 | fn do_modify<'f>(holder: &'f mut Holder<'f>) {
    |              -- lifetime `'f` defined here
936 | 
937 |     modify_val(holder);
    |     ------------------
    |     |          |
    |     |          first mutable borrow occurs here
    |     argument requires that `*holder` is borrowed for `'f`
938 |     let mut v = holder.hold.remove("$return").unwrap();
    |                 ^^^^^^^^^^^ second mutable borrow occurs here

非常感谢任何帮助!!!

弄清楚了,本质上 BasicVal<'a> 导致 Holder 在循环的连续迭代中可变地借用自身,因此删除生命周期几乎是唯一的解决方案