Explain rust lifetimes with below code

fn ok<'lt>(e1:&'lt mut  &'lt mut String, e2:&'lt mut & mut String){}


fn main(){

    let mut z = String::from("suneel");
    let mut x = String::from("dalavaie");

    let mut q = &mut z;
    let mut f= &mut x;

    ok(  &mut q , &mut f);

   println!("{}",x);
    println!("{}",q);


}

i know how to solve this..but what i want to know is working details of lifetimes

fn ok<'lt>(e1: &'lt mut &'lt mut String, e2: &'lt mut &mut String) {}

fn main() {
    let mut z = String::from("suneel");
    let mut x = String::from("dalavaie");

    let mut q = &mut z; // Call this 'q
    let mut f = &mut x; // Call this 'f

    ok(&mut q, &mut f); // Call the two outer lifetimes 'lt
    
    // `&mut T` is invariant in `T`, meaning any lifetimes in `T` can't be
    // automatically "shrunk".  So you must be passing exactly
    //     &'lt mut &'q mut String, &'lt mut &'f mut String
    //
    // And so we must have `'q == 'lt` as per the signature of `ok`.
    //
    // Additionally, `'f: 'lt` or the outer reference could point to something
    // invalid.  This means wherever `'lt` is still valid, `'f` must also still
    // be valid.
    
    // This is a use of `x` requires that `'f` is no longer valid, because
    // `f: &'f mut x` must be exclusive for all of `'f`.
    println!("{}", x);
    
    // This use of `q` requires that `q: &'lt mut z` must still be valid.  And
    // because `'f: 'lt`, that means `'f` must be valid too.  This conflicts
    // with the `println!` above.
    println!("{}", q);
}

(You'll have to scroll the code window to see all the comments.[1])

Oh, and the second error is because once you have a &'lt mut &'lt mut String, you can never use the inner &mut String again.


  1. Or at least, I have to. ↩︎

4 Likes

thank you!
one more thing what is the relation between q and f according to ok func signature

or

what if change func signature from

fn ok<'lt>(e1: &'lt mut &'lt mut String, e2: &'lt mut &mut String) {}

this to

fn ok<'lt>(e1: &'lt mut &'lt mut String, e2: &'lt mut &'lt mut String) {}

The lifetime of f's type has to be at least as large as that of q's type. So if you're still using q, x has to remain exclusively borrowed via f.

It effectively doesn't matter in your particular example, since you can't use q after the call to ok anyway. But in this version, the last use of q conflicts with (the attempt of) forcing 'f to be no longer valid.

2 Likes

What happening under the hood when 'lifetime parameter is there

Compiler creates a large system of equations and checks whether this system has any solutions, and if there's none - this is an error. What kind of "under the hood" you're asking?

3 Likes

I am asking about which thing is replaced in lifetime parameter

Not an expert. But add the official documentation link (maybe a little outdated) to what @Cerber-Ursi means

What is a lifetime and how does it interact with the borrow checker

To start with, we will consider lifetimes as a set of points in the control-flow graph; later in the RFC we will extend the domain of these sets to include "skolemized" lifetimes, which correspond to named lifetime parameters declared on a function. If a lifetime contains the point P, that implies that references with that lifetime are valid on entry to P. Lifetimes appear in various places in the MIR representation:

  • The types of variables (and temporaries, etc) may contain lifetimes.
  • Every borrow expression has a designated lifetime.

...
src: RFC 2094 NLL: What is a lifetime and how does it interact with the borrow checker

From my understanding, lifetime paramters are constrains from the APIs which are meant to be stable to users. And when anyone calls these APIs, they write source code that downgrades to MIR where lifetime constrains from your source code and APIs (that are from yours and others) are laid out to be checked. When there are no conflicts after resolving all the constrains, your code passes; if any, lifetime error pops up.

So lifetime parameters in an API represents lifetime relations in there, and become multiple constrains.

1 Like

In case it wasn't clear from the other answers, lifetimes don't exist after compilation. They're part of the borrow checker analysis done during compile time. They determine what code is allowed to compile, but not what the compiled code does.

3 Likes

More formally: lifetimes are not part of the program you wrote [1], they are part of proof of correctness added to your program.

In theory you may add these to any unsafe language, like C++. That's, essentially, what is proposed to save C++. Heck, Ada did that (by borrowing ideas from Rust, of course), why C++ couldn't?

But I don't think it'll work. C/C++ culture is full I don't care about subscript error, I just want my program to run guys. And that is why C and C++ are doomed, not because there are some technical deficiency in them.


  1. Most of the time. There are not-yet-fully-specified corner cases where they may affect codegen, but usually they are just removed. ↩︎

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.