为什么借阅检查器会提示此代码?

fn foo<'a>(v: &mut Vec<&'a str>, buf: &'a mut String) {
    loop {
        foo(v, buf);
    }
}

error[E0499]: cannot borrow `*buf` as mutable more than once at a time
 --> src/main.rs:3:16
  |
3 |         foo(v, buf);
  |                ^^^ mutable borrow starts here in previous iteration of loop
4 |     }
5 | }
  | - mutable borrow ends here

如果删除生存期限制,则代码可以正常编译。
fn foo(v: &mut Vec<&str>, buf: &mut String) {
    loop {
        foo(v, buf);
    }
}

这不是Mutable borrow in a loop的重复项,因为在我的情况下没有返回值。

我很确定我的最终目标无法在安全的Rust中实现,但是现在我想更好地了解借用检查器的工作原理,并且我不明白为什么在此参数之间添加有效期限制会延长借用的期限代码。

最佳答案

具有显式生存期'a的版本将Vec的生存期与buf的生存期联系起来。当重新借用VecString时,这会引起麻烦。当参数在循环中传递给foo时,会发生重新借入:

fn foo<'a>(v: &mut Vec<&'a str>, buf: &'a mut String) {
    loop {
        foo(&mut *v, &mut *buf);
    }
}

这是由编译器隐式完成的,以防止在循环中调用foo时消耗参数。如果参数实际上已移动,则在第一次递归调用foo之后,将无法再使用它们(例如,连续调用foo)。

强制buf到处移动可解决该错误:
fn foo<'a>(v: &mut Vec<&'a str>, buf: &'a mut String) {
    foo_recursive(v, buf);
}

fn foo_recursive<'a>(v: &mut Vec<&'a str>, buf: &'a mut String) -> &'a mut String{
    let mut buf_temp = buf;
    loop {
        let buf_loop = buf_temp;
        buf_temp = foo_recursive(v, buf_loop);
        // some break condition
    }
    buf_temp
}

但是,一旦您尝试实际使用buf,事情就会再次崩溃。这是您的示例的简化版本,说明了为什么编译器禁止连续可变地借用buf:
fn foo<'a>(v: &mut Vec<&'a str>, buf: &'a mut String) {
    bar(v, buf);
    bar(v, buf);
}

fn bar<'a>(v: &mut Vec<&'a str>, buf: &'a mut String) {
    if v.is_empty() {
        // first call: push slice referencing "A" into 'v'
        v.push(&buf[0..1]);
    } else {
        // second call: remove "A" while 'v' is still holding a reference to it - not allowed
        buf.clear();
    }
}

fn main() {
    foo(&mut vec![], &mut String::from("A"));
}

bar的调用等效于您的示例中对foo的递归调用。再次,编译器提示*buf不能一次多次作为可变借借。所提供的bar实现显示,关于bar的生存期规范将允许以v进入无效状态的方式来实现此功能。编译器仅通过查看bar的签名即可理解,来自buf的数据可能潜在地流入v,并且拒绝该代码可能不安全,而不论bar的实际实现如何。

关于loops - 具有生存期限制的递归函数内部循环中的借入检查器错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51505176/

10-11 07:39