0

Run the following code:

struct B<'a> {
    data: &'a mut &'a str,
}

pub fn test_1() {
    let mut s = "hello";
    *B {
        data: &mut s,
    }.data = "world";
    println!("s: {}", s);
}

I got the following error:

error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
  --> src\test_ref.rs:14:23
   |
12 |         data: &mut s,
   |               ------ mutable borrow occurs here
13 |     }.data = "world";
14 |     println!("s: {}", s);
   |                       ^
   |                       |
   |                       immutable borrow occurs here
   |                       mutable borrow later used here

The same goes for the following:

pub fn test_1() {
    let mut s = "hello";
    {
        *B {
            data: &mut s,
        }.data = "world";
    }
    println!("s: {}", s);
}
error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
  --> src\test_ref.rs:28:23
   |
25 |             data: &mut s,
   |                   ------ mutable borrow occurs here
...
28 |     println!("s: {}", s);
   |                       ^
   |                       |
   |                       immutable borrow occurs here
   |                       mutable borrow later used here

But when I use two lifetime, the compilation passed.

struct A<'a, 'b> {
    data: &'a mut &'b str,
}
pub fn test() {
    let mut s = "hello";
    let a = A {
        data: &mut s,
    };
    *a.data = "world";
    println!("s: {}", s);
}

Now I want to know why the first piece of code has not been compiled and the second piece of code has passed. In the second piece of code, there is only one more lifetime annotation?

  • Where is the confusion? You tell the compiler the mutable borrow lasts as long as the inner reference, so as long as the inner reference sticks around, so will the mutable borrow. – cafce25 Jan 03 '23 at 08:55
  • Can we use covariant and invariant to explain this ? – wengang yang Jan 03 '23 at 09:46
  • `&'a mut Foo<'a>` is always wrong (and `&'a…` is a special case of `Foo<'a>`, so `&'a mut &'a…` is also always wrong) because it links the two lifetimes in a way that makes them unusable. Note that if you remove the `mut`: `&'a Foo<'a>` is usually wrong but there are a couple of rare cases where it is legitimate. – Jmb Jan 03 '23 at 10:20
  • Yes, the mutable reference makes the lifetime invariant. But I don't think it will help you to understand the problem. – Chayim Friedman Jan 03 '23 at 10:23
  • See also https://stackoverflow.com/questions/74408718/keeping-track-of-a-child-struct-who-called-a-method-in-rust#comment131361126_74408718 and https://stackoverflow.com/questions/71689224/rust-lifetimes-understanding-lifetime-error-for-mutable-reference-to-self – Jmb Jan 03 '23 at 10:25
  • 1
    Does this answer your question? [Why does this mutable borrow live beyond it's scope?](/q/66252831/2189130) – kmdreko Jan 03 '23 at 14:57
  • I added another case. – wengang yang Jan 03 '23 at 15:49

1 Answers1

0

I think in the first test, only one of the lifetime is 'a, according to &'a mut T in 'a is covariant, and T is invariant, so in finally data: &'a mut &'a str lifetime 'a is 'static, 'static means that at all times. In the last test, 'a in data: &'a mut &'b str due to 'a covariance Corresponds to a lifetime less than s, so eventually s can be referenced again.

Variance of types is automatically determined as follows:

Type Variance in 'a Variance in T

&'a T covariant covariant

&'a mut T covariant invariant

https://doc.rust-lang.org/reference/subtyping.html#variance

The following code can also be compiled (I removed the mut) :

struct B<'a> {
    data: &'a &'a str,
}

pub fn test_1() {
    let mut s = "hello";
    {
        let x = B {
            data: &s,
        } ;//.data = "world";
    }
    println!("s: {}", s);
}

In this example 'a is covariant, because [&'a T covariant covariant], So 'a is no longer 'static: 'static covariant to 'a.