借款检查员为什么抱怨此代码?
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中无法实现的,但是现在我想更好地了解借位检查器的工作原理,并且我不明白为什么在参数之间添加有效期限制会延长有效期。借用此代码。
答案 0 :(得分:2)
具有显式生存期'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
的实际实现如何,都将代码拒绝为潜在的不安全代码