The version with the explicit lifetime 'a
ties the lifetime of the Vec
to the lifetime of buf
. This causes trouble when the Vec
and the String
are reborrowed. Reborrowing occurs when the arguments are passed to foo
in the loop:
fn foo<'a>(v: &mut Vec<&'a str>, buf: &'a mut String) {
loop {
foo(&mut *v, &mut *buf);
}
}
This is done implicitly by the compiler to prevent the arguments from being consumed when foo
is called in the loop. If the arguments were actually moved, they could not be used anymore (e.g. for successive calls to foo
) after the first recursive call to foo
.
Forcing buf
to be moved around resolves the error:
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
}
However, things will break again as soon as you try to actually use buf
. Here is a distilled version of your example demonstrating why the compiler forbids successive mutable borrows of 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"));
}
The calls to bar
are the equivalents to the recursive calls to foo
in your example. Again the compiler complains that *buf
cannot be borrowed as mutable more than once at a time. The provided implementation of bar
shows that the lifetime specification on bar
would allow this function to be implemented in such a way that v
enters an invalid state. The compiler understands by looking at the signature of bar
alone that data from buf
could potentially flow into v
and rejects the code as potentially unsafe regardless of the actual implementation of bar
.
fn foo<'a, 'b, 'c>(v: &'a mut Vec<&'b str>, buf: &'c mut String)
. The difference with your version that doesn't work is that you have re-used a lifetime for two of the arguments, tying them together. Neither of them can outlive the other. – Peter HallRc<String>
for the buf instead of the&mut String
. Really depends on what you are really trying to do here. – Peter Hall