Failed to conclude the rules of nested types, reborrow.
Consider the following,
fn main() {
let mut i: i32 = 8;
let _j = {
let x = &mut i;
&*x
};
// does x still alive here?
j;
}
Failed to conclude the rules of nested types, reborrow.
Consider the following,
fn main() {
let mut i: i32 = 8;
let _j = {
let x = &mut i;
&*x
};
// does x still alive here?
j;
}
No, x
does not exist after it went out of scope, but j
can still be valid at that point because j
has a reference to i
, not to x
.
So, j still has a reference to i?
But this time, fails
fn main() {
let mut i: i32 = 8;
let _j = {
let x = &mut i;
let y = &x;
&**y
};
j;
}
Well, y
is a reference to x
, not to i
, so y
cannot be valid when x
goes out of scope. Unfortunately, the borrow rules for flattening are this:
&'short &'long mut T → &'short T
And here the short lifetime is the lifetime of x
, not of i
.
So it's a limitation of the reference flattening operation.
Note that when the inner reference is immutable, the flattening operation is different. In that situation, it works like this:
&'short &'long T → &'long T
So your code would compile if x
was an immutable reference.
The difference between the two cases has to do with the fact that mutable references are generally guaranteed to have exclusive access to the thing they point at, which is necessary because otherwise things like Vec::clear
could make other references into the vector dangling. This kind of exclusivity contains does not apply to immutable references, so they can have a more powerful flattening operation.
But that is where reborrow takes place. The following, both x and &**y
are references to i; &**y
shadows x;
fn main() {
let mut i: i32 = 8;
let _j = {
let x = &mut i;
let y = &x;
&**y;
println!("{:p}", &**y);
println!("{}", *x);
};
}
so, why
fn main() {
let mut i: i32 = 8;
let _j = {
let x = &mut i;
let y = &x;
&**y
};
j;
}
&**y can't shadow x?
Rust has some rules for how flattening operations behave lifetime-wise. These rules are chosen because they reject all incorrect code. However, Rust does not guarantee that it accepts all correct code, and while your code doesn't do anything wrong, it violates the rules for flattening double references, and thus it is rejected.
fn main() {
let mut i: i32 = 8;
let j = {
let x = &mut i;
*&x
};
let k = &i; // fails because there is still a mut reference, e.g. seems &mut x is still alive
j;
}
Well, yes, your &mut i
expression borrows i
mutably, and the mutable borrow lasts until the last use of anything derived from x
.
If it is true, then *&x is the temporary, under life time expand rule?
And, if a temporary lifetime is expanded, then any nested variant is also expanded?
But
fn main() {
let mut i: i32 = 8;
let _j = {
let x = &mut i;
let y = &x;
&**y
};
j;
}
fails because x does not live long enough, e.g. y's life has been expanded, but not x.
e.g. Only direct nested variant will be expanded.
However, can't explain why this can be compiled.......
fn main() {
let mut i: i32 = 8;
let _j = {
let x = &i;
let y = &x;
&**y
};
j;
}
I talked about this case in my previous post above:
Yes, but I don't know it is just a rule or a result of a calculating of the temporary life time expand rules like my previous post.
The conflict is totally the following
this show x outlives it's block
this shows x does not outlive it's block....
(A move of) x
can outlive the block if you transfer ownership outside of the block, like returning a locally created variable from a function:
let j = { let x = &mut i; x };
j;
And that indeed the borrow is still alive due to using j
:
let j = { let x = &mut i; x };
let k = &i; // fails
j;
And this is pretty much what your top example attempts:
let j = { let x = &mut i; *&x };
let k = &i; // fails
j;
Except there is an additional error because you can't move from outside the temporary shared reference you create. (But the borrow checker still sees that if you could, the borrow was flowing into j
.)
Your bottom example is quite different:
let j = {
let x = &mut i; // &'a mut i
let y = &x; // &'x &'a mut i where 'x cannot outlive variable x
&**y // &'y i where 'a: 'y, 'x: 'y
}; // &'y i flows into j
j; // j is used so 'y must be valid here
// but x has been dropped, so 'x is not valid here, so 'x: 'y fails
For one, you're not trying to move x
out of the block in any way. But also, you're performing a nested reborrow. You can read more about reborrowing here; in this case, the important part is that the reference you return has a lifetime that's limited by y
's borrow of x
. And that borrow can't outlive the block, because x
does not outlive the block.
(The citation is the technical reference for the @alice's 'short 'long
explainations.)
What if x
is &i
instead of &mut i
?
let j = {
let x = &i; // &'a i
let y = &x; // &'x &'a i where 'x cannot outlive variable x
&**y // &'y i where 'a: 'y <-- LESS CONSTRAINED
}; // &'y i flows into j
j; // j is used so 'y must be valid here
// Which also means 'a must be valid here. Which is fine!
Because of the lack of mut
, the borrow that flows into j
isn't constrained to the inside block.
Now, you've made some comments along the lines of "this borrow error outside the block indicates that x
lives longer than the block". So there's a chance you're also thinking "this code working means x
lives longer than the block, because of the 'a: 'y
constraint ('a
is valid outside the block)".
However, that's not true. It just means that when you borrowed i
originally:
let x = &i; // &'a i
That x
was assigned a borrow that last longer than x
itself (longer than the block). There is no contradiction here; after all, we do things like this frequently:
fn f() {
let x: &'static str = "foo";
// x certainly does not live forever
}
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.