I have found a case where manually inlining a function changes the way the borrow-checker treats it, such that it no longer compiles. Presumably it is relying on the information in the function signature. How can I provide this information in the inlined version?
How I think it's working
Let 'a
and 'b
be lifetimes with 'a
shorter than 'b
(which can be written 'b: 'a
).
Suppose I have a p: &'b mut f32
. I can borrow p
briefly (with &mut p
) to obtain q: &'a mut &'b mut f32
.
- Have I correctly understood that
&'a mut &'b mut f32
is equivalent to &'a mut &'a mut f32
because 'b: 'a
?
I can then dereference q
(with *q
) to obtain r: &'a mut f32
. I can write to the f32
via r
(with *r = something
), and I can later (outside lifetime 'a
) read back the value via p
(with *p
).
With a function call
Here is some working code that I think uses the above sequence:
fn reborrow<'a, 'b: 'a>(q: &'a mut &'b mut f32) -> &'a mut f32 {
*q
}
fn main() {
let mut x: f32 = 3.142;
let mut p = &mut x;
{
let q = &mut p;
let r = reborrow(q);
*r = 2.718;
}
assert_eq!(*p, 2.718);
}
(Replacing *q
with q
in the body of reborrow()
also works, because Rust inserts the necessary dereference if it is missing).
Manually inlined
If I manually inline the reborrow()
call, it no longer compiles:
fn main() {
let mut x: f32 = 3.142;
let mut p = &mut x;
{
let q = &mut p;
let r = *q; <-- ERROR REPORTED HERE.
*r = 2.718;
}
assert_eq!(*p, 2.718);
}
error[E0507]: cannot move out of borrowed content
Who took away my toys? What is the type inference thinking/missing?
Can I annotate the let
binding somehow to make the compiler infer the same types as in the previous version?
Some other attempts
Here's another version that works, but which doesn't define the name r
:
fn main() {
let mut x: f32 = 3.142;
let mut p = &mut x;
{
let q = &mut p;
**q = 2.718;
}
assert_eq!(*p, 2.718);
}
Here's a work-around that defines the name r
and works, but does not use the same sequence of borrows and dereferences:
fn main() {
let mut x: f32 = 3.142;
let mut p = &mut x;
{
let q = &mut p;
let r = &mut **q;
*r = 2.718;
}
assert_eq!(*p, 2.718);
}
I made a playground combining all four versions.
See Question&Answers more detail:
os