rustlifetimevarianceinvariance

How to analyze generic lifetime when it is relevant to variance


 0 // code snippet 1
 1
 2 struct MutStr<'a >{
 3     s: &'a mut &'a str,
 4 }
 5 
 6 fn main() {
 7     let mut s: &'static str = "hello";
 8     *MutStr{
 9         s: &mut s,
10     }.s = "world";
11     println!("{}", s);
12 }

Rust Playground link of code snippet 1

The code snippnet 1 above varies from Rust for Rustacean Ch1 listing 1-11 where I use 'a to replace two lifetimes 'a and 'b , and this code can not compile, whereas I can not tell the reason:(

I can analyze some simple code such as the following one (from Programming Rust Verson 2 page 120):

 0 // code snippet 2
 1 
 2 struct S<'a> {
 3     x: &'a i32,
 4     y: &'a i32
 5 }
 6
 7 fn main() {
 8     let x = 10;
 9     let r;
10     {
11         let y = 20;
12         {
13             let s = S {x: &x, y: &y};
14             r = s.x;
15         }
16     }
17     println!("{}", r);
18 }

Rust Playground link of code snippet 2

I denote lifetime of x, y and r with 'x , 'y and 'r respectively:

'x 'y 'r
line: from 8 to 18 line: from 11 to 16 line: from 9 to 17

When instantiating s in line 13, we are requiring 'x:'a ('x outlives 'a) 'y:'a , and when assignment r = s.x happens in line 14 , we are requiring 'a:'r . Nevertheless, this is impossible for the reason that 'y:'a conflicts with 'a:'r (see table above, 'y is shorter than 'r ) so that rustc can not find a deterministic lifetime for generic lifetime 'a satisfying those conflicting conditions.

Update: I am expecting a analyze process for code snippet 1 similar to the above one, or a general inference method when encountering lifetime problems:)

I have read this article , knowing some basic concepts about variance, such as covariance invariance and contravariance . And I kind of think my question relate to that, but don't know how to use that to analyze code snippet 1.


Solution

  • I have read this article, knowing some basic concepts about variance, such as covariance invariance and contravariance. And I kind of think my question relate to that, but don't know how to use that to analyze code snippet 1.

    You are on the right track, the difference does lie with lifetime variance. There is a table in the Rust Reference 10.5 Subtyping and Variance that I think is helpful:

    Type Variance in 'a Variance in T
    &'a T covariant covariant
    &'a mut T covariant invariant

    In your second snippet, your references are immutable meaning the lifetime associated with them can be shortened as necessary. A reference to the variable y cannot lengthen its lifetime so the reference to x must be shortened. An thus the reference bound to r is tied to the lifetime of y and therefore you get an error when you try to use r after y has gone out of scope.

    In the first snippet however, you have a mutable reference to a &'a str. If you look at the table above, you'll see that types referenced by a mutable reference are invariant and since the type is itself a &'a str, that means that 'a is invariant. This means, unlike in the second snippet, the compiler can not shorten the lifetime of 'a at all. So when you try to use s to make a MutStr, the compiler sees that you're passing a &'static str that it cannot shorten, so 'a must be 'static. But then it tries to reconcile that 'a is also linked to the variable s which is not 'static, so you get the error.