fn main() {
struct Foo<'a, T> {
a: &'a mut T,
}
let p1 = 1;
let mut p2 = &p1;
{
let p3 = 2;
let mut p4 = &p3;
let mut f = Foo {
a: &mut p4,
};
f.a = &mut p2;
println!("{}", f.a);
}
println!("{}", p2);
}
While run this program, it meets error such as:
error[E0597]: `p3` does not live long enough
--> src/main.rs:10:19
|
10 | let mut p4 = &p3;
| ^^^ borrowed value does not live long enough
...
16 | }
| - `p3` dropped here while still borrowed
17 | println!("{}", p2);
| -- borrow later used here
-- borrow later used here
this is my first question: why exist these errors?
If I annotate the last line, such as :
fn main() {
struct Foo<'a, T> {
a: &'a mut T,
}
let p1 = 1;
let mut p2 = &p1;
{
let p3 = 2;
let mut p4 = &p3;
let mut f = Foo {
a: &mut p4,
};
f.a = &mut p2;
println!("{}", f.a);
}
// println!("{}", p2);
}
It runs successfully. By refer to rustonomicon,
f.a = &mut p2;
&'a mut T is covariant over 'a and invariant over T, It should compile unsuccesfully. But successfully, why?
If I annotate the last line, It should compile unsuccesfully.
The core of the problem is that f has a single fixed type Foo<'a, &'b i32> and by the variance rules for mutable references, &'b i32 is invariant and thus 'b is invariant.
However, f is used with T as two separate lifetimes via p2 and p4. How does the compiler choose? Well it cannot shorten the lifetime used by p2 into that of p4, because then p2 can be modified to reference something of a smaller lifetime and thus p2 can dangle at the last println! (consider what would happen if you added *f.a = &p3; right after assigning f.a to &mut p2). The only option is for the lifetime used by p4 to be widened to match p2.
Since p4 must match the lifetime of p2, the assignment from p3 is now too short, so you get the error you see.
The second example works because the lifetime used by p2 does not extend after the println! in the inner block, so p3 can satisfy that lifetime.
Related
I created two example codes, one using references of a Box and the other using borrow_mut of a RefCell and they don't work the same, to my surprise.
use std::cell::RefCell;
#[derive(Debug)]
struct A {
a: usize,
}
fn main() {
let m;
let mut n = Box::new(A{a:5});
{let a = &mut n; m = &mut a.a;}
*m = 6;
println!("{}", m);
println!("{:?}", n);
let mm;
let mut nn = RefCell::new(A{a:5});
{let mut aa = nn.borrow_mut(); mm = &mut aa.a;}
*mm = 6;
println!("{:?}", mm);
println!("{:?}", nn);
}
I'm getting an error saying the aa borrowed value does not live long enough. &mut aa.a should be &mut usize so nothing to do with a RefCell and should work just like the Box example. Why am I getting this error?
The problem is that while the borrow &mut n is just a pure reference, nn.borrow_mut() is not. It returns a RefMut that implements Drop - because it needs to mark the RefCell as no longer borrowed after it is dropped. Therefore, the compiler can extend the lifetime of &mut n as it doesn't do anything when dropped, but it cannot do so for nn.borrow_mut() because it alters the behavior of the program.
This question already has an answer here:
What is lifetime elision in very simple terms?
(1 answer)
Closed 3 months ago.
I have this function that borrows two individual elements from a vector. It works as expected:
fn borrow_mut_two<T>(v: &mut [T], i: usize, j: usize) -> (&mut T, &mut T) {
assert!(i < j);
let (left, right) = v.split_at_mut(j);
(&mut left[i], &mut right[0])
}
fn test() {
let mut v = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
let i = 2;
let j = 5;
let (ref_a, ref_b) = borrow_mut_two(&mut v, i, j);
*ref_a += 1;
*ref_b += 5;
assert_eq!(*ref_a, i + 1);
assert_eq!(*ref_b, j + 5);
}
What I'm trying to understand is why the following code doesn't compile:
fn test() {
let mut v = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
let i = 2;
let j = 5;
let (ref_a, ref_b) = borrow_mut_two(&mut v, i, j);
// Added this line:
let (other_ref_a, other_ref_b) = borrow_mut_two(&mut v, i, j);
*ref_a += 1;
*ref_b += 5;
assert_eq!(*ref_a, i + 1);
assert_eq!(*ref_b, j + 5);
}
It seems it works in a safe manner, because it doesn't allow me to mutably borrow the same elements twice (or potentially other elements).
My question is how does the compiler know that this is unsafe (and therefore reject compiling it)?
The compiler errors are:
229 | let (ref_a, ref_b) = borrow_mut_two(&mut v, i, j);
| ------ first mutable borrow occurs here
230 | let (other_ref_a, other_ref_b) = borrow_mut_two(&mut v, i, j);
| ^^^^^^ second mutable borrow occurs here
As far as I know the return value of borrow_mut_two is the tuple (&mut T, &mut T), which may or may not be a borrow to self, but it seems the compiler does know it's borrowing self. My assumptions may be wrong though 🙂.
The only thing that comes to mind is that Rust automatically adds the lifetime:
fn borrow_mut_two<'a, T>(v: &'a mut [T], i: usize, j: usize) -> (&'a mut T, &'a mut T)
Which would mean that in my test function, 'a is still alive (i.e. self mutably borrowed) due to the first call to borrow_mut_two while the second call happens, and that's how it detects a second mutable borrow.
But I'd like to confirm if this is correct.
Extra
It could be related to this part of the book. The only difference I see is that my function returns a tuple. So the question can be reduced to: Does Rust's second lifetime elision rule also add 'a to each element of a tuple? If so, then my doubt is solved:
The first rule is that the compiler assigns a lifetime parameter to
each parameter that’s a reference. In other words, a function with one
parameter gets one lifetime parameter: fn foo<'a>(x: &'a i32); a
function with two parameters gets two separate lifetime parameters: fn
foo<'a, 'b>(x: &'a i32, y: &'b i32); and so on.
The second rule is that, if there is exactly one input lifetime
parameter, that lifetime is assigned to all output lifetime
parameters: fn foo<'a>(x: &'a i32) -> &'a i32.
Yes, your understanding is correct - due to lifetime elision, the following two function signatures are equivalent:
fn borrow_mut_two<T>(v: &mut [T], i: usize, j: usize) -> (&mut T, &mut T)
fn borrow_mut_two<'a, T>(v: &'a mut [T], i: usize, j: usize) -> (&'a mut T, &'a mut T)
Because the lifetimes of the output are tied to the lifetime of v, the compiler knows that v is still mutably borrowed until those derived references stop being used.
The issue in your second code is that you are trying to borrow mutably v while an other mutable borrow is still alive, just as the compiler indicates to you. To be more precise, the compiler sees the following constraints:
fn test() {
// ...
let (ref_a, ref_b) = borrow_mut_two(&'3 mut v, i, j); // ------+
// ^^^^^ ^^^^^ ^^^^^^^ Let's call // |
// +---+ this lifetime '3 // |
// | // |
// Let's call the lifetime of these mutable borrows '1 // |
// Added this line: // +- '1
let (other_ref_a, other_ref_b) = borrow_mut_two(&'4 mut v, i, j); // -+ '2 |
// ^^^^^^^^^^^ ^^^^^^^^^^^ // | |
// +------+ Let's call the lifetimes of these borrows '2 // -+ |
*ref_a += 1; // |
*ref_b += 5; // ------+
}
First of all, '3 and '4 cannot outlive the lifetime of v, but this isn't very important in this case so we can forget about it. Then, due to the signature of borrow_mut_two, '1='3 and '2='4. Furthermore, since we are talking about mutable borrows, there can be at most one at a time. Finally, I have indicated the minimum span of '1 and '2.
When the compiler tries to enforce all these constraints, it will fail, because clearly '1 and '2 cannot be disjoint while living at least for as much time as they are useful.
In fact, it is sometimes possible to make this kind of layout work, because '1 is never used when '2 is used, and '2's span is included in '1's. In these cases, the compiler says the borrows are stacked, and it could accept them if '2 was a reborrow of '1. It is logically the case, but split_at_mut prevents the compiler from understanding this because internally it uses unsafe code on which the compiler can't reason as easily as it would otherwise.
I'm trying to figure out a Rust lifetime issue and after boiling it down a bunch, I realized that I have no idea how I would explicitly annotate the lifetimes of r, x2, and _arr in foo:
struct X<'a> {
_v: &'a mut i32,
}
fn main() {
let mut v1 = 0;
let x1 = X { _v: &mut v1 };
foo(x1);
}
fn foo(x1: X) {
let mut v2 = 1;
let r = &mut v2;
let x2 = X { _v: r };
let _arr = [x1, x2];
}
I'm going to work off of the assumption that the part of your code that got cut off is the following:
struct X<'a> {
_v: &'a mut i32,
}
In that case, let's dissect what's going on in foo a little bit.
fn foo(x1: X) {
// Lives until the end of the function, call its lifetime 'v2
let mut v2 = 1;
// Borrow v2 for some lifetime 'r which is no longer than 'v2
// so r is of type &'r mut i32
let r = &mut v2;
let x2 = X { _v: r }; // x2 is of type X<'r>
let _arr = [x1, x2]; // ??
}
The confusion probably comes from the fact that x1 seems to have an ambiguous lifetime in its type. This is because we didn't explicitly specify the lifetime in x1: X. This is a Rust 2018 idiom, and I personally recommend not doing this. You can add #![deny(rust_2018_idioms)] to the top of your crate root and the compiler will point out these ambiguities to you and force you to be more explicit. What happens here is that the function declaration gets de-sugared to the following:
fn foo<'x1>(x1: X<'x1>) { ... }
Now it is implied that the lifetime 'x1 extends at least through the body of foo, and this makes sense because it kind of has to. If something which lived for 'x1 was freed in the middle of foo (disregarding how something like that could even happen), then that would defeat the point of using lifetimes for memory safety.
So that being said, let's revisit this line:
let _arr = [x1, x2];
We know that x2 is of the type X<'r> and that 'r extends at most to the end of foo (since 'r is no longer than 'v2 and 'v2 extends to the end of foo). Moreover, we know that x1 is of the type X<'x1> and that 'x1 extends at least to the end of foo. This means that 'x1 is at least as long as 'r. Since lifetimes are covariant, this means that X<'x1> is a sub-type of X<'r>, since whenever we require a value of the type X<'r> we can safely substitute in one of type X<'x1> instead. So what happens is _arr is given the type [X<'r>; 2], and upon construction the lifetime on x1 is shortened to 'r.
We can actually test this hypothesis to see if it's correct. What would happen if the compiler wasn't allowed to shorten that lifetime? In other words, what if the lifetime on X was not covariant. If we modify X as follows then its lifetime type parameter is made invariant:
struct X<'a> {
_v: &'a mut i32,
_invariant: PhantomData<fn(&'a ()) -> &'a ()>
}
And sure enough, after adding the _invariant field in the appropriate places to make the code compile, we receive the following error:
|
14 | fn foo<'x1>(x1: X<'x1>) {
| --- lifetime `'x1` defined here
15 | let mut v2 = 1;
16 | let r = &mut v2;
| ^^^^^^^ borrowed value does not live long enough
17 | let x2 = X { _v: r, _invariant: PhantomData };
| - this usage requires that `v2` is borrowed for `'x1`
18 | let _arr = [x1, x2];
19 | }
| - `v2` dropped here while still borrowed
Now how do we know the compiler wasn't extending the lifetime 'r to 'x1 in the first place? Well if it was doing that, then we could modify foo to do the following, which would unequivocally cause a use-after-free:
fn foo<'x1>(x1: X<'x1>) -> &'x1 mut i32 {
let mut v2 = 1;
let r = &mut v2;
let x2 = X { _v: r };
let _arr = [x1, x2];
_arr[1]._v
}
And sure enough if you try the code above it fails to compile with the reason given being that we're returning a reference to a local variable. Moreover, if you try returning _arr[0]._v, then you get the exact same error.
Sub-typing and variance can be pretty hard to grasp and it's not something you need to fully understand to be an effective Rust programmer. Nonetheless, it is very interesting and you can learn more about it here in the Rustonomicon.
In the below code example, I'm trying to increment the member variable a of the struct X via a mutable reference to it, in four different ways. Here, the compiler gives the following error for the line denoted by B:
error[E0502]: cannot borrow `*x` as immutable because it is also borrowed as mutable
--> src\main.rs:17:23
|
17 | *x.get_a_mut() += x.get_a(); //B DOESN'T COMPILE
| ------------------^--------
| || |
| || immutable borrow occurs here
| |mutable borrow occurs here
| mutable borrow later used here
If it is a problem to use a mutable and an immutable reference to a in the same expression, why does C and D compile?
struct X {
a: i64,
}
impl X {
pub fn get_a_mut(&mut self) -> &mut i64 {
return &mut self.a;
}
pub fn get_a(&self) -> &i64 {
return &self.a;
}
}
fn my_fn(x: &mut X) {
*x.get_a_mut() += 5; //A
*x.get_a_mut() += x.get_a(); //B DOESN'T COMPILE
*x.get_a_mut() += 2 * x.get_a(); //C
*x.get_a_mut() = x.get_a() + x.get_a(); //D
}
fn main() {
let mut x = X { a: 50 };
my_fn(&mut x);
}
According to += documentation, you are calling something like add_assign(lhs: &mut i64, rhs: &i64) in case B and something like add_assign(lhs: &mut i64, rhs: i64) in cases A, C and D.
In case A, rhs is a constant, different from x.a; no problem.
In case C, rhs is a temporary (the result of 2 * x.get_a()) and does not need to keep a reference on x.a to exist; no problem.
In case D, rhs is a temporary (the result of x.get_a() + x.get_a()) and does not need to keep a reference on x.a to exist; no problem.
But when it comes to case B, rhs is a reference on x.a; then this call uses both a mutable (lhs) and immutable (rhs) reference on the same data (x.a) at the same time, which is forbidden.
You could eventually clone rhs: *x.get_a_mut() += x.get_a().clone().
I am learning Rust and I've run into some confusing behaviour. The following code compiles fine and works as expected (edit: added code other than test function, previously omitted):
struct Container<'a> {
contents : &'a mut i32,
}
fn main() {
let mut one = Container { contents: &mut 5 };
test(&mut one);
println!("Contents: {}",one.contents);
}
fn test<'a>(mut x : &'a mut Container) {
*x.contents += 1;
let y = x;
*y.contents += 1;
x = y;
println!("{:?}",*x.contents)
}
Now in the statement
let y = x;
the type is inferred. Because x is of type &'a mut Container, I thought that this would be equivalent:
let y: &'a mut Container = x;
But when I do that, the compiler takes issue:
test_3.rs:25:5: 25:10 error: cannot assign to `x` because it is borrowed
test_3.rs:25 x = y;
^~~~~
test_3.rs:23:33: 23:34 note: borrow of `x` occurs here
test_3.rs:23 let y: &'a mut Container = x;
How is x not borrowed by that point in the correctly working example? I tested by omitting the line x = y; from the correctly working version and the compiler said:
test_3.rs:24:13: 24:14 note: `x` moved here because it has type `&mut Container<'_>`, which is moved by default
So I'm getting a move when I don't explicitly define the type but a borrow otherwise. What is going on, how do I get the same behavior as before while explicitly giving the type, and what is causing move behavior in one case but borrow in the other?
Edited with full program
When you do
let y = x;
a move happens. x is emptied, so to speak, and ownership is transferred to y.
When you do either of
let y: &mut _ = x;
let y: &'a mut _ = x;
x is reborrowed to aid matching the lifetimes. This roughly translates to
let y: &mut _ = &mut *x;
let y: &'a mut _ = &mut *x;
This leaves x non-empty, holding an aliased mutable borrow. Assigning to it thus must wait for y to be destroyed. Alternatively, you can pre-move it
let tmp = x;
let y: &'a mut _ = tmp;
I'll admit it's nonobvious behaviour, and it's a shame that you can't borrow the contents of a value without borrowing the whole value.