为什么借阅检查器会提示此代码?
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
的生存期联系起来。当重新借用Vec
和String
时,这会引起麻烦。当参数在循环中传递给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/