Beginner: trying to understand lifetimes

Oooh, nice find :slight_smile: I figured there was probably an exception but I couldn't think of one!

1 Like

Oh! I didn't follow the thread for the last two days. Now I see a lot of great answers. Thanks y'all! :slight_smile:

2 Likes

slight tangent: is there a good way to print discussions like this? I tried so I can sit with it when I have a bit of downtime later - but several pages end up blank :frowning:

Copy and paste into a word processor?

The problem is the way discourse loads (and unloads) posts using javascript. I don't know of any easy way to print it all. The hard way might be to load Beginner: trying to understand lifetimes and parse it into printable form.

EDIT: Actually try pressing ctrl + p. That seems to work for me. It's not very discoverable but there we go. It is listed under "keyboard shortcuts" in the hamburger menu.

1 Like

@Yandros - I like the idea of coloring the variables too! Could use a bit more contrast between the text and the hilight but it's a great idea!

Can't wait till a few years from now when there's like a VSCode plugin or whatever that does all this highlighting in the IDE... that'll be amazing!

That's interesting... you're saying name and job_title don't have the same exact lifetime? I would think they do since they are both 'a ...?

1 Like

I think this is a very important question, that is needed to understand the most significant feature of the rust language, let me try to give a short and clear explanation.

3 concepts:

  • variable lifetime
  • scope
  • lifetime parameter

When talking about lifetimes, we need to think on the variables we declare in the code as if the program would be executing. One given variable will have a lifetime from the moment the program reaches its declaration until this one is no longer valid.
A scope is a code region that allows variable declarations and by default encloses the entire lifetime of such. An example is a region between two curly braces, but more on this later.
Other programming languages terminate variables when we reach the closing curly brace. But in Rust, we may move the value earlier than this point. In this scenario, the scope is not equal to a lifetime.
Another case is when we return an object by value, and therefore is moved out of the current scope: in such case, the lifetime will exceed the scope.
A third scenario is when we call a method on a temporary value (as a result of chaining method calls, for example). The scope of a temporary variable is the one expression that it executes, and will be part of a single line of code. If the value is no moved, the whole lifetime of such an object will be in that one line of code.

Regarding the lifetimes parameters: Rust programing language can compute correctness of lifetimes by keeping track of it. This is done at the simplest level we can think of, and is keeping track of lifetimes on a scope. Every lifetime coming in is correct and the compiler will guarantee that the scope is correct on itself. When declaring a function, we must declare lifetimes parameters to instruct the compiler on which references have what lifetimes. This is needed because we may have more than one reference in the parameters + return, and they may have different lifetimes. If we would not have lifetime parameters, then the compiler would have to assume that everything has the same lifetime and the computation of the borrow checker would be not possible.

Hope this helps. One example that helps me to think about lifetimes is the following exercise:
Write a function that accepts a collection of structs, and returns a reference to a field from one of the structs.

1 Like

I just shamelessly copied @carols10cents's great teaching idea, but with both very little time and image-editing skills (or lack thereof), hence the poor quality :sweat_smile:

Yes, if both are 'a, then they are both equal.

I guess the expression "the shorter of the two references' lifetimes" was referring to the lifetimes given to construct an "instance" of such struct; once both references are united in a Something structure, their (maybe different) lifetimes "shrink" to 'a, where 'a must thus be smaller* than each of these lifetimes.

That is, with the more general case:

struct ConstructionSomething<'name, 'job_title> {
  name: &'name str,
  job_title: &'job_title str,
}

then, (by covariance of borrows w.r.t the lifetime of the borrow),
for any lifetime 'a smaller* than both 'name (noted 'name : 'a) and
'job_title (noted 'job_title : 'a), then
ConstructionSomething<'name, 'job_title'> is a subtype of
ConstructionSomething<'a, 'a>,
i.e., Something<'a>.
So the lifetime parameter 'a of Something<'a> ends up verifying that it is shorter* than each of the lifetimes of the borrows given to construct such struct.

*or equal to
1 Like

This has been a fantastic thread. Thank you all!

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.