0

I tried to make 'long' greater than 'longest'. This obviously should be reported as an error, but it was not reported. Why?

fn test_lifetime<'long, 'longer, 'longest>(a: &'long str, b: &'longer str, c: &'longest str) where
    'long: 'longest
{
    println!("{}, {}, {}", a, b, c);
}

fn main() {
    let longest = String::from("longest");
    {
        let longer = String::from("longer");
        {
            let long = String::from("long");
            test_lifetime(long.as_str(), longer.as_str(), longest.as_str());
        }
    }
}
Anunaki
  • 763
  • 2
  • 8
  • 18
  • `'a: 'b` means `'a` is at least as long as `'b`. The inheritance relation for lifetimes is "A is-a B if A's lifetime is not less than B's" – BallpointBen Oct 28 '22 at 14:23
  • See also: https://stackoverflow.com/questions/42637911/how-can-this-instance-seemingly-outlive-its-own-parameter-lifetime – E_net4 Oct 28 '22 at 14:36

2 Answers2

3

This is not an error for two reasons:

  1. The lifetimes 'long, 'longer and 'longest refer to the borrows, not the values in the variables long, longer and longest;
  2. Rust will try to find lifetimes that satisfy certain constraints. This means that if it compiles, it is possible to find lifetimes that do satisfy these constraints.

So, in this case, a possible set of lifetimes that satisfies all the constraints is simply:

 fn main() {
    let longest = String::from("longest");
    {
        let longer = String::from("longer");
        {
            let long = String::from("long");
                                 // ------\                                   
            test_lifetime(       //       |
                long.as_str(),   //       |
                longer.as_str(), //       |-- 'long = 'longer = 'longest
                longest.as_str() //       |
            );                   //       |
                                 // ------/
        }
    }
}

Indeed, longest.as_str() just creates a &str that has to live less than longest, but it can be as short as the one created by long.as_str(). This is also compatible with your constraints, since 'a: 'a for all 'a (subtyping is reflexive).

jthulhu
  • 7,223
  • 2
  • 16
  • 33
1

If you change the code a bit to something like this:

fn test_lifetime<'long, 'longer, 'longest>(a: &'long str, b: &'longer str, c: &'longest str) -> &'longest str
where
    'long: 'longest
{
    println!("{}, {}, {}", a, b, c);
    return c
}

fn main() {
    let s: &str;
    let longest: String = String::from("longest");
    {
        let longer: String = String::from("longer");
        {
            let long: String = String::from("long");
            s = test_lifetime(long.as_str(), longer.as_str(), longest.as_str());
        }
    }
    println!("{}", s);
}

It will not pass the compiler. The reason is that now the compiler cannot shrink the lifetime 'longest to 'long in order to satisfy the lifetime bound, because the compiler must return c using its "original" lifetime.

In the code above if you don't assign the return value of test_lifetime, or don't have the println at the end that uses s, the code still compiles, because the returned value will be in the scope of lifetime 'long, which means that compiler can shrink 'longest to 'long (a.k.a "covariance").

fred xia
  • 151
  • 3