Rust lifetime in cycle
How to get this example to compile without copying an array or multiple calls b()
per iteration - b()
has to do some expensive parsing?
This is not the complete code I wrote, but it illustrates the problem I had. Here's Test
trying to do some thread parsing work. c()
is a parsing function, it returns Some
when the parsing was successful. b()
is a function that tries to read more data from a stream when c()
it cannot parse using the available data. The return value is a chunk in self.v
containing the parsed range.
struct Test {
v: [u8; 10],
index: u8,
}
impl Test {
fn b(&mut self) {
self.index = 1
}
fn c(i: &[u8]) -> Option<&[u8]> {
Some(i)
}
fn a(&mut self) -> &[u8] {
loop {
self.b();
match Test::c(&self.v) {
Some(r) => return r,
_ => continue,
}
}
}
}
fn main() {
let mut q = Test {
v: [0; 10],
index: 0,
};
q.a();
}
On compilation, it produces the following check validation error:
error[E0502]: cannot borrow `*self` as mutable because `self.v` is also borrowed as immutable --> <anon>:17:13 | 17 | self.b(); | ^^^^ mutable borrow occurs here 18 | 19 | match Test::c(&self.v) { | ------ immutable borrow occurs here ... 24 | } | - immutable borrow ends here
If I change a()
to:
fn a(&mut self) -> Option<&[u8]> {
loop {
self.b();
if let None = Test::c(&self.v) {
continue
}
if let Some(r) = Test::c(&self.v) {
return Some(r);
} else {
unreachable!();
}
}
}
It then runs, but with the obvious disadvantage of calling the parse function c()
twice.
I understand that changing self
while the return value depends on it is unsafe, however I don't understand why the immutable loan for self.v
is still alive in the next iteration when we tried to call b()
again.
source to share
Right now "Rustc cannot" handle "conditional borrowing". See comment from Gankro at 21906 .
It cannot assign the correct lifetime to borrow if only one execution path completes the cycle.
I can suggest this workaround, but I'm not sure if it's optimal:
fn c(i: &[u8]) -> Option<(usize, usize)> {
Some((0, i.len()))
}
fn a(&mut self) -> &[u8] {
let parse_result;
loop {
self.b();
match Test::c(&self.v) {
Some(r) => {
parse_result = r;
break;
}
_ => {}
}
}
let (start, end) = parse_result;
&self.v[start..end]
}
You can plot the parsing result using array indices and convert them to references outside of the loop.
Another option is to resort to unsafe
to split the lifetime. I am not an expert on the safe use of unsafe, so pay attention to the comments of others.
fn a(&mut self) -> &[u8] {
loop {
self.b();
match Test::c(&self.v) {
Some(r) => return unsafe{
// should be safe. It decouples lifetime of
// &self.v and lifetime of returned value,
// while lifetime of returned value still
// cannot outlive self
::std::slice::from_raw_parts(r.as_ptr(), r.len())
},
_ => continue,
}
}
}
source to share