Reputation: 55
I have this code that doesn't compile:
impl BytePacketBuffer {
fn read(&mut self) -> Result<u8, &str> {
if self.pos >= 512 {
return Err("End of buffer".into());
}
let res = self.buf[self.pos];
self.pos += 1;
Ok(res)
}
fn read_u16(&mut self) -> Result<u16, &str> {
let res = ((self.read()? as u16) << 8) | (self.read()? as u16);
Ok(res)
}
}
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/byte_packet_buffer.rs:53:51
|
52 | fn read_u16(&mut self) -> Result<u16, &str> {
| - let's call the lifetime of this reference `'1`
53 | let res = ((self.read()? as u16) << 8) | (self.read()? as u16);
| ------------ ^^^^^^^^^^^ second mutable borrow occurs here
| |
| first mutable borrow occurs here
| returning this value requires that `*self` is borrowed for `'1`
But if I modify the return type from &str
to String
, it compiles without error. Can anybody explain why I get an error when returning &str
but not when returning String
?
Upvotes: 3
Views: 421
Reputation: 70910
@cafce25 explained well how lifetime elision leads to incorrect lifetime, but this still does not explain why this fails. Even with the incorrect lifetimes, when we borrow self
for the &str
it is the error case, and the error case is immediately returning from the function and thus does not borrow self
further.
Well, this is because the ?
operators desugars roughly into (simplified, but the actual desugaring doesn't matter here):
match expr {
Ok(v) => v,
Err(e) => return Err(From::from(e)),
}
And it is well known that the borrow checker is overly conservative w.r.t. branches. It extends borrow in return expression to the rest of the function, causing the &str
to live even when it cannot really live.
If you compile it with Polonius (cargo +nightly rustc -- -Zpolonius
), it compiles successfully.
Upvotes: 3
Reputation: 27258
Because you did not specify any lifetimes Rust will infer them according to the elision rules to be this:
fn read<'a>(&'a mut self) -> Result<u8, &'a str>
which means it will infer self
to be borrowed for as long as you keep the Result
or anything it contained around.
To fix it you could just specify the lifetime of the returned str
because it's always static anyways.
fn read(&mut self) -> Result<u8, &'static str>
Upvotes: 4