I'm running into a Rust borrow checker error that I believe is a limitation of the current implementation of non-lexical lifetimes. The code I want to write looks something like this:
struct Thing {
value: i32
}
impl Thing {
fn value(&self) -> &i32 {
&self.value
}
fn increment(&mut self) {
self.value += 1;
}
}
/// Increments the value of `thing` if it is odd, and returns a reference to the value.
fn increment_if_odd(thing: &mut Thing) -> &i32 {
let ref_to_value = thing.value();
if (*ref_to_value % 2) == 0 {
return ref_to_value;
}
thing.increment(); // fails to compile because the immutable borrow `ref_to_value` is still alive
thing.value()
}
First question: am I right in thinking that this code is 100% safe and the borrow checker is being too conservative? The branch that returns ref_to_value
doesn't mutate thing
so the reference is guaranteed to be valid, and the other branch doesn't use ref_to_value
at all. (I understand that if I replace return ref_to_value;
with return thing.value();
it will compile, but in my actual code the value
method is expensive.)
It seems I can fix this by "laundering" the reference through a pointer:
if (*ref_to_value % 2) == 0 {
return unsafe {
&*(ref_to_value as *const i32)
}
}
Second question: is this trivially safe? I've never used unsafe before so I'm nervous.
I guess a third question: is there a way to rewrite this in safe Rust? The constaint is that value
should only be called once on the non-mutating path.
thing
that therefore so doesref_to_value
? That's weird. Even with the extra scope brackets from @Jetpropelled it doesn't work because of such. That's what I'm reading from your link @Peplum – Waldackincrement
operation onto a newtype for the value, which is obtained by mutably borrowing theThing
. – Samoyedicvalue
method now returns an accessor for the value rather than a reference to the value itself. – Samoyedic