Reputation: 39477
I have the following code:
pub fn read_packet<'a>(buf: &'a mut [u8]) -> &'a [u8] {
loop {
read_exact(buf);
if let Some(packet) = to_packet(buf) {
return packet;
}
}
}
fn read_exact(_: &mut [u8]) {
todo!()
}
fn to_packet<'a>(_: &'a [u8]) -> Option<&'a [u8]> {
todo!()
}
I get the following error:
error[E0502]: cannot borrow `*buf` as mutable because it is also borrowed as immutable
--> src/lib.rs:3:9
|
1 | pub fn read_packet<'a>(buf: &'a mut [u8]) -> &'a [u8] {
| -- lifetime `'a` defined here
2 | loop {
3 | read_exact(buf);
| ^^^^^^^^^^^^^^^ mutable borrow occurs here
4 |
5 | if let Some(packet) = to_packet(buf) {
| --- immutable borrow occurs here
6 | return packet;
| ------ returning this value requires that `*buf` is borrowed for `'a`
I think it should work because:
read_exact
completes on line 3.to_packet
returns Some
then the value is returned to the caller.to_packet
is over at the end of the loop. So it is free to be taken mutable borrow of in the next iteration.Can somebody please let me know why this doesn't work?
It seems like a current borrow checker limitation. I tried using Polonius in the nightly and it works fine with
RUSTFLAGS=-Zpolonius cargo +nightly check
Upvotes: 7
Views: 1052
Reputation: 654
pub fn read_packet<'a>(buffer: &'a mut [u8]) -> &'a [u8] { |
let buf = buffer; |
loop { |
read_exact(buf); \/
if let Some(packet) = to_packet(buf) { 'a _
return packet; |
} |
} |
}
fn read_exact(_: &mut [u8]) {
todo!()
}
fn to_packet<'b>(_from: &'b [u8]) -> Option<&'b [u8]> {
todo!()
}
Compile error:
|
1 | pub fn read_packet<'a>(buffer: &'a mut [u8]) -> &'a [u8] {
| -- lifetime `'a` defined here
...
4 | read_exact(buf);
| ^^^^^^^^^^^^^^^ mutable borrow occurs here
5 | if let Some(packet) = to_packet(buf) {
| --- immutable borrow occurs here
6 | return packet;
| ------ returning this value requires that `*buf` is borrowed for `'a`
With Non Lexical Lifetime (NLL):
packet
lifetime to be 'a
.packet
is 'a
, so buf
(to_packet) has to be also 'a
.'a
is valid for the entire function. The loop makes the shared reference lifetime conflicts with the exclusive reference lifetime at the next iteration.The conflict can be reproduced without the loop. This snippet doesn't compile for the same reason that buff
is 'a
. And we see that the root cause is the conditional return. Again 'a has to be valid for the entire function.
pub fn read_packet<'a>(buf: &'a mut [u8]) -> &'a [u8] {
if let Some(packet) = to_packet(buf) { 'a _
return packet; |
} |
read_exact(buf); |
return &[0]; \/
}
fn read_exact(_: &mut [u8]) {
todo!()
}
fn to_packet<'b>(_: &'b [u8]) -> Option<&'b [u8]> {
todo!()
}
Compile error:
|
1 | pub fn read_packet<'a>(buf: &'a mut [u8]) -> &'a [u8] {
| -- lifetime `'a` defined here
2 | if let Some(packet) = to_packet(buf) {
| --- immutable borrow occurs here
3 | return packet;
| ------ returning this value requires that `*buf` is borrowed for `'a`
4 | }
5 | read_exact(buf);
| ^^^^^^^^^^^^^^^ mutable borrow occurs here
With NLL, the lifetime is infered like you thought it would be.
pub fn read_packet<'a>(buffer: &'a mut [u8]) -> &'a [u8] {
let buf = buffer;
loop {
read_exact(buf);
if let Some(packet) = to_packet(buf) { 'x _
return packet; |
} \/
}
}
fn read_exact(_: &mut [u8]) {
todo!()
}
fn to_packet<'b>(_from: &'b [u8]) -> Option<&'b [u8]> {
todo!()
}
Upvotes: 1
Reputation: 42698
It is a compiler limitation atm. You could refactor to something like:
pub fn read_packet<'a>(buf: &'a mut [u8]) {
loop {
if read_exact(buf) {
break;
}
}
}
fn is_packet(a: &[u8]) -> bool {
true
}
fn read_exact<'a>(a: &'a mut [u8]) -> bool {
is_packet(a)
}
fn to_packet<'a>(_: &'a [u8]) -> Option<&'a [u8]> {
todo!()
}
fn process_packet<'a>(buf: &'a mut [u8]) {
read_packet(buf);
let _packet = to_packet(buf);
}
Upvotes: 2