rustlifetimevariance

How to understand Covariance in rust?


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?


Solution

  • 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.