A few days ago, there was a question where someone had a problem with linked lifetimes of a mutable reference to a type which contained borrowed data itself. The problem was supplying a reference to the type with a borrow of the same lifetime as the borrowed data inside the type.
I tried to recreate the problem:
struct VecRef<'a>(&'a Vec<u8>);
struct VecRefRef<'a>(&'a mut VecRef<'a>);
fn main() {
let v = vec![8u8, 9, 10];
let mut ref_v = VecRef(&v);
create(&mut ref_v);
}
fn create<'b, 'a>(r: &'b mut VecRef<'a>) {
VecRefRef(r);
}
Example code
I explicitly annotated 'b here in create(). This does not compile:
error[E0623]: lifetime mismatch
--> src/main.rs:12:15
|
11 | fn create<'b, 'a>(r: &'b mut VecRef<'a>) {
| ------------------
| |
| these two types are declared with different lifetimes...
12 | VecRefRef(r);
| ^ ...but data from `r` flows into `r` here
The lifetime 'b is something like 'b < 'a and therefore violating the constraint in the VecRefRef<'a> to be of exactly the same lifetime as the referred to VecRef<'a>.
I linked the lifetime of the mutable reference with the borrowed data inside the VecRef<'a>:
fn create<'a>(r: &'a mut VecRef<'a>) {
VecRefRef(r);
}
Now it works. But why? How was I even able to supply such a reference? The mutable reference r inside create() has the lifetime of VecRef<'a> not 'a. Why wasn't the problem pushed up to the calling side of the function create()?
I noticed another thing I did not understand. If I use an immutable reference inside the VecRefRef<'a> struct, it somehow does not matter any more when supplying a reference with a different lifetime of 'a:
struct VecRef<'a>(&'a Vec<u8>);
struct VecRefRef<'a>(&'a VecRef<'a>); // now an immutable reference
fn main() {
let v = vec![8u8, 9, 10];
let mut ref_v = VecRef(&v);
create(&mut ref_v);
}
fn create<'b, 'a>(r: &'b mut VecRef<'a>) {
VecRefRef(r);
}
Example code
This works as opposed to the first example where VecRefRef<'a> took a mutable reference to a VecRef<'a>. I know that mutable references have different aliasing rules (no aliasing at all) but what has that to do with the linked lifetimes here?
Warning: I'm speaking from a level of expertise that I don't really have. Given the length of this post, I'm probably wrong a large number of times.
TL;DR: Lifetimes of top-level values are covariant. Lifetimes of referenced values are invariant.
Introducing the problem
You can simplify your example significantly, by replacing VecRef<'a> with &'a mut T.
Further, one should remove main, since it's more complete to talk about the general behaviour of a function than some particular lifetime instantiation.
Instead of VecRefRef's constructor, let's use this function:
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
Before we go further, it's important to understand how lifetimes get implicitly cast in Rust. When one assigns a pointer to another explicitly annotated name, lifetime coercion happens. The most obvious thing this allows is shrinking the lifetime of the top-level pointer. As such, this is not a typical move.
Aside: I say "explicitly annotated" because in implicit cases like let x = y or fn f<T>(_: T) {}, reborrowing doesn't seem to happen. It is not clear whether this is intended.
The full example is then
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
use_same_ref_ref(reference);
}
which gives the same error:
error[E0623]: lifetime mismatch
--> src/main.rs:5:26
|
4 | fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
| ------------------
| |
| these two types are declared with different lifetimes...
5 | use_same_ref_ref(reference);
| ^^^^^^^^^ ...but data from `reference` flows into `reference` here
A trivial fix
One can fix it by doing
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a>(reference: &'a mut &'a mut ()) {
use_same_ref_ref(reference);
}
since the signatures are now logically the same. However, what is not obvious is why
let mut val = ();
let mut reference = &mut val;
let ref_ref = &mut reference;
use_ref_ref(ref_ref);
is able to produce an &'a mut &'a mut ().
A less trivial fix
One can instead enforce 'a: 'b
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
use_same_ref_ref(reference);
}
This means that the lifetime of the outer reference is at least as large as the lifetime of the inner one.
It's not obvious
why &'a mut &'b mut () is not castable to &'c mut &'c mut (), or
whether this is better than &'a mut &'a mut ().
I hope to answer these questions.
A non-fix
Asserting 'b: 'a does not fix the problem.
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a, 'b: 'a>(reference: &'a mut &'b mut ()) {
use_same_ref_ref(reference);
}
Another, more surprising fix
Making the outer reference immutable fixes the problem
fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) {}
fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) {
use_same_ref_ref(reference);
}
And an even more surprising non-fix!
Making the inner reference immutable doesn't help at all!
fn use_same_ref_ref<'c>(reference: &'c mut &'c ()) {}
fn use_ref_ref<'a, 'b>(reference: &'a mut &'b ()) {
use_same_ref_ref(reference);
}
BUT WHY??!
And the reason is...
Hold on, first we cover variance
Two very important concepts in computer science are covariance and contravariance. I'm not going to use these names (I'll be very explicit about which way I'm casting things) but those names are still very useful for searching the internet.
It's very important to understand the concept of variance before you can understand the behaviour here. If you've taken a university course that covers this, or you can remember it from some other context, you're in a good position. You might still appreciate the help linking the idea to lifetimes, though.
The simple case - a normal pointer
Consider some stack positions with a pointer:
║ Name │ Type │ Value
───╫───────────┼─────────────────────┼───────
1 ║ val │ i32 │ -1
───╫───────────┼─────────────────────┼───────
2 ║ reference │ &'x mut i32 │ 0x1
The stack grows downwards, so the reference stack position was created after val, and will be removed before val is.
Consider that you do
let new_ref = reference;
to get
║ Name │ Type │ Value
───╫───────────┼─────────────┼───────
1 ║ val │ i32 │ -1
───╫───────────┼─────────────┼───────
2 ║ reference │ &'x mut i32 │ 0x1
───╫───────────┼─────────────┼───────
3 ║ new_ref │ &'y mut i32 │ 0x1
What lifetimes are valid for 'y?
Consider the two mutable pointer operations:
Read
Write
Read prevents 'y from growing, because a 'x reference only guarantees the object stays alive during the scope of 'x. However, read does not prevent 'y from shrinking since any read when the pointed-to value is alive will result in a value independent of the lifetime 'y.
Write prevents 'y from growing also, since one cannot write to an invalidated pointer. However, write does not prevent 'y from shrinking since any write to the pointer copies the value in, which leaves it independent of the lifetime 'y.
The hard case - a pointer pointer
Consider some stack positions with a pointer pointer:
║ Name │ Type │ Value
───╫───────────┼─────────────────────┼───────
1 ║ val │ i32 │ -1
───╫───────────┼─────────────────────┼───────
2 ║ reference │ &'a mut i32 │ 0x1
───╫───────────┼─────────────────────┼───────
3 ║ ref_ref │ &'x mut &'a mut i32 │ 0x2
Consider that you do
let new_ref_ref = ref_ref;
to get
║ Name │ Type │ Value
───╫─────────────┼─────────────────────┼───────
1 ║ val │ i32 │ -1
───╫─────────────┼─────────────────────┼───────
2 ║ reference │ &'a mut i32 │ 0x1
───╫─────────────┼─────────────────────┼───────
3 ║ ref_ref │ &'x mut &'a mut i32 │ 0x2
───╫─────────────┼─────────────────────┼───────
4 ║ new_ref_ref │ &'y mut &'b mut i32 │ 0x2
Now there are two questions:
What lifetimes are valid for 'y?
What lifetimes are valid for 'b?
Let's first consider 'y with the two mutable pointer operations:
Read
Write
Read prevents 'y from growing, because a 'x reference only guarantees the object stays alive during the scope of 'x. However, read does not prevent 'y from shrinking since any read when the pointed-to value is alive will result in a value independent of the lifetime 'y.
Write prevents 'y from growing also, since one cannot write to an invalidated pointer. However, write does not prevent 'y from shrinking since any write to the pointer copies the value in, which leaves it independent of the lifetime 'y.
This is the same as before.
Now, consider 'b with the two mutable pointer operations
Read prevents 'b from growing, since if one was to extract the inner pointer from the outer pointer you would be able to read it after 'a has expired.
Write prevents 'b from growing also, since if one was to extract the inner pointer from the outer pointer you would be able to write to it after 'a has expired.
Read and write together also prevent 'b from shrinking, because of this scenario:
let ref_ref: &'x mut &'a mut i32 = ...;
{
// Has lifetime 'b, which is smaller than 'a
let new_val: i32 = 123;
// Shrink 'a to 'b
let new_ref_ref: &'x mut &'b mut i32 = ref_ref;
*new_ref_ref = &mut new_val;
}
// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a mut i32 = *ref_ref;
// Oops, we have an &'a mut i32 pointer to a dropped value!
Ergo, 'b cannot shrink and it cannot grow from 'a, so 'a == 'b exactly. This means &'y mut &'b mut i32 is invariant in the lifetime 'b.
OK, does this solve our questions?
Remember the code?
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a, 'b>(reference: &'a mut &'b mut ()) {
use_same_ref_ref(reference);
}
When you call use_same_ref_ref, a cast is attempted
&'a mut &'b mut () → &'c mut &'c mut ()
Now note that 'b == 'c because of our discussion about variance. Thus we are actually casting
&'a mut &'b mut () → &'b mut &'b mut ()
The outer &'a can only be shrunk. In order to do this, the compiler needs to know
'a: 'b
The compiler does not know this, and so fails compilation.
What about our other examples?
The first was
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a>(reference: &'a mut &'a mut ()) {
use_same_ref_ref(reference);
}
Instead of 'a: 'b, the compiler now needs 'a: 'a, which is trivially true.
The second directly asserted 'a: 'b
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
use_same_ref_ref(reference);
}
The third asserted 'b: 'a
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a, 'b: 'a>(reference: &'a mut &'b mut ()) {
use_same_ref_ref(reference);
}
This does not work, because this is not the needed assertion.
What about immutability?
We had two cases here. The first was to make the outer reference immutable.
fn use_same_ref_ref<'c>(reference: &'c &'c mut ()) {}
fn use_ref_ref<'a, 'b>(reference: &'a &'b mut ()) {
use_same_ref_ref(reference);
}
This one worked. Why?
Well, consider our problem with shrinking &'b from before:
Read and write together also prevent 'b from shrinking, because of this scenario:
let ref_ref: &'x mut &'a mut i32 = ...;
{
// Has lifetime 'b, which is smaller than 'a
let new_val: i32 = 123;
// Shrink 'a to 'b
let new_ref_ref: &'x mut &'b mut i32 = ref_ref;
*new_ref_ref = &mut new_val;
}
// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a mut i32 = *ref_ref;
// Oops, we have an &'a mut i32 pointer to a dropped value!
Ergo, 'b cannot shrink and it cannot grow from 'a, so 'a == 'b exactly.
This can only happen because we can swap the inner reference for some new, insufficiently long lived reference. If we are not able to swap the reference, this is not a problem. Thus shrinking the lifetime of the inner reference is possible.
And the failing one?
Making the inner reference immutable does not help:
fn use_same_ref_ref<'c>(reference: &'c mut &'c ()) {}
fn use_ref_ref<'a, 'b>(reference: &'a mut &'b ()) {
use_same_ref_ref(reference);
}
This makes sense when you consider that the problem mentioned before never involves any reads from the inner reference. In fact, here's the problematic code modified to demonstrate that:
let ref_ref: &'x mut &'a i32 = ...;
{
// Has lifetime 'b, which is smaller than 'a
let new_val: i32 = 123;
// Shrink 'a to 'b
let new_ref_ref: &'x mut &'b i32 = ref_ref;
*new_ref_ref = &new_val;
}
// new_ref_ref is out of scope, so ref_ref is usable again
let ref_ref: &'a i32 = *ref_ref;
// Oops, we have an &'a i32 pointer to a dropped value!
There was another question
It's been quite long, but think back to:
One can instead enforce 'a: 'b
fn use_same_ref_ref<'c>(reference: &'c mut &'c mut ()) {}
fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
use_same_ref_ref(reference);
}
This means that the lifetime of the outer reference is at least as large as the lifetime of the inner one.
It's not obvious
why &'a mut &'b mut () is not castable to &'c mut &'c mut (), or
whether this is better than &'a mut &'a mut ().
I hope to answer these questions.
We've answered the first bullet-pointed question, but what about the second? Does 'a: 'b permit more than 'a == 'b?
Consider some caller with type &'x mut &'y mut (). If 'x : 'y, then it will be automatically cast to &'y mut &'y mut (). Instead, if 'x == 'y, then 'x : 'y holds already! The difference is thus only important if you wish to return a type containing 'x to the caller, who is the only one able to distinguish the two. Since this is not the case here, the two are equivalent.
One more thing
If you write
let mut val = ();
let mut reference = &mut val;
let ref_ref = &mut reference;
use_ref_ref(ref_ref);
where use_ref_ref is defined
fn use_ref_ref<'a: 'b, 'b>(reference: &'a mut &'b mut ()) {
use_same_ref_ref(reference);
}
how is the code able to enforce 'a: 'b? It looks on inspection like the opposite is true!
Well, remember that
let reference = &mut val;
is able to shrink its lifetime, since it's the outer lifetime at this point. Thus, it can refer to a lifetime smaller than the real lifetime of val, even when the pointer is outside of that lifetime!
The mutable reference r inside create() has the lifetime of VecRef<'a> not 'a
This is a common source of confusion. Check this function definition:
fn identity<'a, T>(val: &'a T) -> &'a T { val }
In a function definition, 'a is a generic lifetime parameter, which parallels a generic type parameter (T). When the function is called, the caller decides what the concrete values of 'a and T will be. Let's look back at your main:
fn main() {
let v = vec![8u8, 9, 10]; // 1 |-lifetime of `v`
let mut ref_v = VecRef(&v); // 2 | |-lifetime of `ref_v`
create(&mut ref_v); // 3 | |
}
v will live for the entire run of main (1-3), but ref_v only lives for the two final statements (2-3). Note that ref_v refers to a value that outlives it. If you then take a reference to ref_v, you have a reference to something that lives from (2-3) that itself has a reference to something that lives from (1-3).
Check out your fixed method:
fn create<'a>(r: &'a mut VecRef<'a>)
This says that for this function call, the reference to the VecRef and the reference it contains must be the same. There is a lifetime that can be picked that satisfies this — (2-3).
Note that your structure definition currently requires that the two lifetimes be the same. You could allow them to differ:
struct VecRefRef<'a, 'b: 'a>(&'a mut VecRef<'b>);
fn create<'a, 'b>(r: &'a mut VecRef<'b>)
Note that you have to use the syntax 'b: 'a to denote that the lifetime 'b will outlive 'a.
If I use an immutable reference [...], it somehow does not matter any more
This I'm less sure about. I believe that what is happening is that because you have an immutable borrow, it's OK for the compiler to reborrow at a smaller scope for you automatically. This allows the lifetimes to match. As you pointed out, a mutable reference cannot have any aliases, even ones with a smaller scope, so the compiler can't help in that case.
Related
Small example to illustrate the problem. The following compiles:
fn main() {
let value: u32 = 15;
let mut ref_to_value: &u32 = &0;
fill_object(&mut ref_to_value, &value);
println!("referring to value {}", ref_to_value);
}
fn fill_object<'a>(m: &mut &'a u32, v: &'a u32) {
*m = v;
}
Now, the following gives a compile time error about mutable borrow followed by immutable borrow:
fn fill_object<'a>(m: &'a mut &'a u32, v: &'a u32) {
*m = v;
}
fill_object(&mut ref_to_value, &value);
| ----------------- mutable borrow occurs here
5 | println!("referring to value {}", ref_to_value);
| ^^^^^^^^^^^^
| |
| immutable borrow occurs here
| mutable borrow later used here
Why? I'm presuming that because I have now specified a lifetime of 'a for the reference to ref_to_value, that mutable reference is now over the entire scope (ie. main). Whereas before, without the 'a lifetime reference, the mutable reference was limited?
I'm looking for clarity on how to think about this.
Your intuition is spot on. With one lifetime,
fn fill_object<'a>(m: &'a mut &'a u32, v: &'a u32) {
*m = v;
}
All three references are required to live for the same length, so if v lives a long time then the mutable reference must as well. This is not intuitive behavior, so it's generally a bad idea to tie together lifetimes like this. If you don't specify any lifetimes, Rust gives each reference a different one implicitly. So the following are equivalent.
fn fill_object(m: &mut &u32, v: &u32)
fn fill_object<'a, 'b, 'c>(m: &'a mut &'b u32, v: &'c u32)
(Note: The inferred lifetime of returned values is a bit more complicated but isn't in play here)
So, your partially-specified lifetimes are equivalent to
fn fill_object<'a>(m: &mut &'a u32, v: &'a u32);
fn fill_object<'a, 'b>(m: &'b mut &'a u32, v: &'a u32);
As a side note, &mut &u32 is a weird type for multiple reasons. Putting aside the fact that u32 is copy (and hence, outside of generics, useless to take immutable references of), a mutable reference to an immutable reference is just confusing. I'm not sure what you're real use case is. If this was just a test example, then sure. But if this is your real program, I recommend considering if you can get off with fill_object(&mut u32, u32), and if you really need the nested reference, you might consider making it a bit easier to swallow with a structure.
struct MyIntegerCell<'a>(&'a u32);
Plus some documentation as to why this is necessary. And then you would have fill_object(&mut MyIntegerCell, u32) or something like that.
I want to write the following function:
fn foo<'a, 'b, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
*rr1 = *rr2;
}
But the compiler complains:
error[E0623]: lifetime mismatch
--> src/lib.rs:2:12
|
1 | fn foo<'a, 'b, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
| ----------- ------------------- these two types are declared with different lifetimes...
2 | *rr1 = *rr2;
| ^^^^ ...but data from `rr2` flows into `rr1` here
My mental model of Rust's lifetimes does not agree that the code is wrong. I read the type of rr2 as "A reference with lifetime 'b to a reference with lifetime 'c to an u32". Thus when I dereference rr2, I get a reference with lifetime 'c to an u32. This should be safe to store in *rr1, which has the same type.
If I require that 'b outlives 'c, it works:
fn foo<'a, 'b: 'c, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
*rr1 = *rr2;
}
This makes me think that the type &'b mut &'c mut u32 means the u32 at the end of the reference chain is only available during the intersection of 'b and 'c.
What is the right explanation for Rust's behavior here? And why do references of references behave this way instead of the way I thought they do?
You cannot dereference a &'b mut &'c mut u32 and get a &'c mut u32 because:
&mut references are not trivially copiable, so you can't copy the &'c mut u32; and
You cannot move out of a reference, so you also can't move the &'c mut u32 (which would leave the outer reference dangling).
Instead, the compiler reborrows the u32 with the outer lifetime, 'b. This is why you get an error message that data from rr2 flows into rr1.
If foo were allowed to compile, you could use it to get two &mut references to the same u32, which is forbidden by the rules of references:
let (mut x, mut y) = (10, 20);
let mut rx = &mut x;
let mut ry = &mut y;
foo(&mut rx, &mut ry); // rx and ry now both refer to y
std::mem::swap(rx, ry); // undefined behavior!
If I require that 'b outlives 'c, it works
Because 'c must already outlive 'b¹, if you require that 'b also outlives 'c, it follows that 'c = 'b. The updated signature is equivalent to this:
fn foo<'a, 'b>(rr1: &'a mut &'b mut u32, rr2: &'b mut &'b mut u32)
That is, you have unified 'c and 'b, and now there's no problem borrowing a &'b mut u32 from rr2 because the inner and outer lifetimes both live for 'b. However, the compiler now won't let you write the broken code in the example I gave earlier, since ry is already borrowed for its entire lifetime.
Interestingly, if you make the inner reference non-mut, it also works:
fn foo<'a, 'b, 'c>(rr1: &'a mut &'c u32, rr2: &'b mut &'c u32) {
*rr1 = *rr2;
}
This is because & references are Copy, so *rr2 is not a reborrow, but actually just a copy of the inner value.
For more information, read:
Why does linking lifetimes matter only with mutable references?
How can I modify a slice that is a function parameter?
¹ It might not be obvious why 'c outlives 'b when there is no explicit 'c: 'b bound. The reason is because the compiler assumes that the type &'b mut &'c mut u32 is well-formed. Well-formedness can become complex (see RFC 1214) but in this case it just means you can't have a reference that's valid for longer ('b) than the thing it references ('c).
I've recently come across a borrow checker message I've never seen before which I'm trying to understand. Here is the code to reproduce it (simplified, real-life example was more complex) - playground:
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { &mut v1 } else { &mut v2 };
dest.push(1);
}
It fails to compile with the following error:
error[E0623]: lifetime mismatch
--> src/main.rs:2:44
|
1 | fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
| ------------ ------------ these two types are declared with different lifetimes...
2 | let dest = if which { &mut v1 } else { &mut v2 };
| ^^^^^^^ ...but data from `v2` flows into `v1` here
...followed by another one about data flowing from v1 into v2.
My question is: what does this error mean? What is data flow and how does it occur between the two variables, given that the code is only pushing Copy data to one of them?
If I follow the compiler and force the lifetimes of v1 and v2 to match, the function compiles (playground):
fn foo<'a>(mut v1: &'a mut Vec<u8>, mut v2: &'a mut Vec<u8>, which: bool) {
let dest = if which { &mut v1 } else { &mut v2 };
dest.push(1);
}
However, on further inspection it turned out that the original code was needlessly complex, left over from when v1 and v2 were actual Vecs, and not references. A simpler and more natural variant is to set dest not to &mut v1 and &mut v2, but to the simpler v1 and v2, which are references to begin with. And that compiles too (playground):
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
In this seemingly equivalent formulation lifetimes of v1 and v2 matching are no longer a requirement.
The problem is that &'a mut T is invariant over T.
First, let's look at the working code:
fn foo(v1: &mut Vec<u8>, v2: &mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
The types of v1 and v2 have elided lifetime parameters. Let's make them explicit:
fn foo<'a, 'b>(v1: &'a mut Vec<u8>, v2: &'b mut Vec<u8>, which: bool) {
let dest = if which { v1 } else { v2 };
dest.push(1);
}
The compiler has to figure out the type of dest. The two branches of the if expression produce values of different types: &'a mut Vec<u8> and &'b mut Vec<u8>. Despite that, the compiler is able to figure out a type that is compatible with both types; let's call this type &'c mut Vec<u8>, where 'a: 'c, 'b: 'c. &'c mut Vec<u8> here is a common supertype of both &'a mut Vec<u8> and &'b mut Vec<u8>, because both 'a and 'b outlive 'c (i.e. 'c is a shorter/smaller lifetime than either 'a or 'b).
Now, let's examine the erroneous code:
fn foo<'a, 'b>(v1: &'a mut Vec<u8>, v2: &'b mut Vec<u8>, which: bool) {
let dest = if which { &mut v1 } else { &mut v2 };
dest.push(1);
}
Again, the compiler has to figure out the type of dest. The two branches of the if expression produce values of types: &'c mut &'a mut Vec<u8> and &'d mut &'b mut Vec<u8> respectively (where 'c and 'd are fresh lifetimes).
I said earlier that &'a mut T is invariant over T. What this means is that we can't change the T in &'a mut T such that we can produce a subtype or supertype of &'a mut T. Here, the T types are &'a mut Vec<u8> and &'b mut Vec<u8>. They are not the same type, so we must conclude that the types &'c mut &'a mut Vec<u8> and &'d mut &'b mut Vec<u8> are unrelated. Therefore, there is no valid type for dest.
What you have here is a variation of this erroneous program:
fn foo(x: &mut Vec<&u32>, y: &u32) {
x.push(y);
}
The error messages used to be a bit more vague but were changed with this pull request. This is a case of Variance, which you can read more about in the nomicon if you are interested. It is a complex subject but I will try my best to explain the quick and short of it.
Unless you specify the lifetimes, when you return &mut v1 or &mut v2 from your if statement, the types are determined by the compiler to have different lifetimes, thus returning a different type. Therefore the compiler can't determine the correct lifetime (or type) for dest. When you explicitly set all lifetimes to be the same, the compiler now understands that both branches of the if statement return the same lifetime and it can figure out the type of dest.
In the example above x has a different lifetime from y and thus a different type.
What is the reason the following code compiles fine, despite both the lifetimes 'a and 'b being independent of each other?
struct Foo<'a> {
i: &'a i32
}
fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
x.i
}
fn main() {}
If I make the reference i in Foo mutable, it gives the following error.
5 | fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
| ----------- -------
| |
| this parameter and the return type are declared with different lifetimes...
6 | x.i
| ^^^ ...but data from `x` is returned here
What is the reason it gives the above error?. Does it consider it's ownership over mutable reference and it sees that something (from Foo) is being taken out (with an independent lifetime), which is not possible, hence the error ?
This code (which I thought would pass) fails too:
struct Foo<'a> {
i: &'a mut i32
}
fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
x.i
}
fn main() {}
fails with error:
error[E0623]: lifetime mismatch
--> src/main.rs:6:5
|
5 | fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
| -----------
| |
| these two types are declared with different lifetimes...
6 | x.i
| ^^^ ...but data from `x` flows into `x` here
But this one passes:
struct Foo<'a> {
i: &'a mut i32
}
fn func<'a: 'b, 'b>(x: &'a Foo<'b>) -> &'b i32 {
x.i
}
fn main() {}
This seems a bit counter-intuitive to me. Here, the outer lifetime ('a) may outlive the inner lifetime ('b). Why is this not an error?
What is the reason the following code compiles fine, despite both the lifetimes 'a and 'b being independent of each other?
fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
x.i
}
The reason is that they are not independent of each other.
The type &'a Foo<'b> would be impossible if 'a outlived 'b. So, implicitly the Rust borrow-checker is inferring that you must have intended that 'b: 'a ('b outlives 'a). So the code above is semantically the same as this:
fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
x.i
}
If I make the reference i in Foo mutable, it gives the following error.
5 | fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
| ----------- -------
| |
| this parameter and the return type are declared with different lifetimes...
6 | x.i
| ^^^ ...but data from `x` is returned here
What is the reason it gives the above error?
When you pass around an immutable reference, it gets copied. In the above example, that means that the &'b i32 can be moved around by itself, and its liveness is not tied to where you got it from. This copied reference always points back to the original address of the data. And that's why the first example works - even if x was dropped, the original reference would still be valid.
When you pass around a mutable reference, it gets moved. A consequence of that is this case is that the reference is still "through" the variable x. If this wasn't the case, x's mutable reference to the contents of Foo could be live at the same time as this new immutable reference - which is not allowed. So, in this case the reference cannot outlive 'a - or put another way: 'a: 'b.
But didn't we already say 'b: 'a? The only conclusion here is that 'a and 'b must be the same lifetime, which is what your previous error message was demanding.
What is the reason it gives the above error?. Does it consider it's
ownership over mutable reference and it sees that something (from Foo)
is being taken out (with an independent lifetime), which is not
possible, hence the error ?
The mutable borrow indeed cannot be moved out of Foo since mutable borrows are not Copy. It is implicitly immutably reborrowed:
fn func <'a, 'b> (x:&'a Foo<'b>) -> &'b i32 {
&*x.i
}
However, this is not the source of the problem. First, here is a summary of the four versions of func (struct Foo has no relevance in my explanation):
version 1 (compiles):
fn func<'a, 'b>(x: &'a &'b i32) -> &'b i32 {
x
}
version 2 (fails to compile):
fn func<'a, 'b>(x: &'a &'b mut i32) -> &'b i32 {
x
}
version 3 (fails to compile):
fn func<'a, 'b: 'a>(x: &'a &'b mut i32) -> &'b i32 {
x
}
version 4 (compiles):
fn func<'a: 'b, 'b>(x: &'a &'b mut i32) -> &'b i32 {
x
}
Version 2 and 3 fail, because they violate the no-aliasing rule which forbids to have a mutable reference and an immutable reference to a resource at the same time. In both versions 'b may strictly outlive 'a. Therefore, &'b mut i32 and &'b i32 could coexist. Version 1 compiles, because the aliasing rules allow multiple immutable references to a resource at the same time. Therefore, &'b i32 may legally coexist with anothor &'b i32.
At first sight, it looks like version 4 should fail since there are again a mutable borrow and an immutable borrow of the same lifetime. The difference to version 2 and 3 is that this time 'a lives at least as long as 'b due to the requirement 'a: 'b, which implies that 'b may not strictly outlive 'a. As long as lifetime 'a lasts the referenced i32 cannot be mutably borrowed a second time (mutable references are not Copy) - the i32 is already mutably borrowed for the func call.
Here is an example demonstrating, how version 2 and 3 could lead to undefined behavior:
fn func<'a, 'b: 'a>(x: &'a &'b mut String) -> &'b str {
unsafe { std::mem::transmute(&**x as &str) } // force compilation
}
fn main() {
let mut s = String::from("s");
let mutref_s = &mut s;
let ref_s = {
let ref_mutref_s = &mutref_s;
func(ref_mutref_s)
};
// use the mutable reference to invalidate the string slice
mutref_s.clear();
mutref_s.shrink_to_fit();
// use the invalidated string slice
println!("{:?}", ref_s);
}
Swapping version 3 with version 4 shows how the in this case still active outer immutable borrow prevents the second mutable borrow. Lifetime 'a on the outer immutable borrow is forced by the new requirement 'a: 'b to be expanded to be equal to lifetime 'b:
error[E0502]: cannot borrow `*mutref_s` as mutable because `mutref_s` is also borrowed as immutable
--> src/main.rs:20:5
|
17 | let ref_mutref_s = &mutref_s;
| -------- immutable borrow occurs here
...
20 | mutref_s.clear();
| ^^^^^^^^ mutable borrow occurs here
...
23 | }
| - immutable borrow ends here
Just to add a suggestion (see the other answers for a detailed explanation of your question):
Whenever possible do not overdesign with lifetimes.
In this case being explicit about all lifetimes implies 4 cases (and quite a lot of thinking!).
Case 1 (compile)
fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'a i32 {
x.i
}
Case 2 (compile)
fn func<'a: 'b, 'b>(x: &'a Foo<'b>) -> &'a i32 {
x.i
}
Case 3 (compile)
fn func<'a: 'b, 'b>(x: &'a Foo<'b>) -> &'b i32 {
x.i
}
Case 4 (do not compile)
fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
x.i
}
If you use anonymous lifetimes and let the compiler build the dependencies between lifetime regions it is as simple as:
fn func<'a>(x: &'a Foo) -> &'a i32 {
x.i
}
I've got a vector of reference-counted RefCells and want to pass a Vec of (mut) references into the RefCells into a function. The references shouldn't need to outlive the function call.
It seems like it should be possible (with just one, something like &*x.borrow_mut() is ok). I tried to keep intermediate vectors of RefMut and &mut to control the lifetimes, but I haven't yet worked out a way to get it to work:
use std::cell::{RefCell,RefMut};
use std::vec::Vec;
use std::rc::Rc;
trait SomeTrait {}
struct Wrapper<'a> {
pub r: &'a mut SomeTrait,
}
fn foo(_: &[Wrapper]) {}
fn main() {
let mut v1: Vec<Rc<RefCell<SomeTrait>>> = unimplemented!();
let mut v_rm: Vec<RefMut<_>> = v1.iter_mut().map(|r| r.borrow_mut()).collect();
let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|ref mut rm| Wrapper{ r: &mut ***rm }).collect();
foo(&v_wrapper[..]);
}
(playground)
There's clearly a lifetime issue:
rustc 1.11.0 (9b21dcd6a 2016-08-15)
error: borrowed value does not live long enough
--> <anon>:17:60
|>
17 |> let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|ref mut rm| Wrapper{ r: &mut ***rm }).collect();
|> ^^^^^^^^^^
note: reference must be valid for the block suffix following statement 2 at 17:107...
--> <anon>:17:108
|>
17 |> let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|ref mut rm| Wrapper{ r: &mut ***rm }).collect();
|> ^
note: ...but borrowed value is only valid for the block at 17:71
--> <anon>:17:72
|>
17 |> let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|ref mut rm| Wrapper{ r: &mut ***rm }).collect();
|> ^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
I do control foo so can alter its API to make things easier, but it's in a different module/crate and I don't really want it to need to know that I keep my SomeTrait objects in an Rc<RefCell<_>>.
While it is certainly possible to write code that starts with a Vec<RefMut<T>> and creates a Vec<&mut T> from that (generic example), I would suggest that you change the signature of foo. Many algorithms do not need the random access provided by slices, and if the function could accept an iterator instead of a slice, you wouldn't need to create two whole additional Vecs, aside from the calling function becoming simpler. I'm thinking of a signature like this
fn foo<I, R>(widgets: I)
where I: IntoIterator<Item=R>,
R: DerefMut<Target=SomeTrait>
{
for widget in widgets {
// ...
}
}
Then all you need is producing an iterator that yields RefMut's, which is easily accomplished with v1.iter_mut().map(|x| x.borrow_mut()). Here's an example.
First, I agree with #delnan that you should switch to an iterator-based interface if you can.
Most of this code is just fine, and after changing foo and Wrapper to be a bit more flexible, I was able to tweak the rest and get it to compile:
use std::cell::{RefCell,RefMut};
use std::vec::Vec;
use std::rc::Rc;
trait SomeTrait {}
struct Wrapper<'a, 'b> where 'b: 'a {
pub r: &'a mut (SomeTrait + 'b),
}
fn foo<'a, 'b>(_: &'a mut [Wrapper<'a, 'b>]) where 'b: 'a {}
fn main() {
let mut v1: Vec<Rc<RefCell<SomeTrait>>> = unimplemented!();
let mut v_rm: Vec<RefMut<_>> = v1.iter_mut().map(|r| r.borrow_mut()).collect();
let mut v_wrapper: Vec<Wrapper> = v_rm.iter_mut().map(|mut rm| Wrapper{ r: &mut **rm }).collect();
foo(&mut v_wrapper[..]);
}
The key thing to understand here is that every trait object type has an implicit lifetime stapled to it, because an impl may contain references. There's no such type as SomeTrait, only SomeTrait + 'a or SomeTrait + 'b or SomeTrait + 'static.
The problem in your code was a mismatch between two things Rust inferred.
Where you wrote Rc<RefCell<SomeTrait>>, Rust assumed you meant Rc<RefCell<SomeTrait + 'static>>.
Where you wrote fn foo(_: &[Wrapper]) {}, different rules applied, and Rust assumed you meant fn foo<'a>(_: &'a [Wrapper<'a> + 'a]).
D'oh. Under those assumptions, the puzzle indeed has no solutions, and that's why I had to loosen things up.
If you don't want that 'b lifetime parameter, you can ditch it and just change 'b to 'static in the one place where it's used (on the type of Wrapper::r). That's less flexible: you'll be limited to SomeTrait impls that have static lifetime.