在具有生命周期边界的递归函数内的循环中借用检查器错误
Borrow checker error in a loop inside a recursive function with lifetime bounds
为什么借用检查器会抱怨此代码?
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 的副本,因为在我的例子中没有 return 值。
我很确定我的最终目标无法在安全 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
的实际实现如何.
为什么借用检查器会抱怨此代码?
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 的副本,因为在我的例子中没有 return 值。
我很确定我的最终目标无法在安全 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
的实际实现如何.