Welcome to module
7, Lifetime parameters are descriptive,
not prescriptive.
In this module, we'll clear up some common misconceptions about lifetime parameters by explaining what the title of this module means.
We'll talk about what lifetime parameters do in terms of describing and what they don't do in terms of prescribing.
We'll discuss the special 'static lifetime and what it does and doesn't do.
And we'll wrap up with some questions to consider when you're getting compiler errors related to lifetimes.
Let's start with what we mean by lifetime parameters being descriptive.
Lifetime parameters are descriptive in the sense that we programmers use them to describe to the compiler how the lifetimes of references are related.
The generic lifetime parameters
we specify
get filled in with concrete lifetimes
when our code is used.
The lifetime parameters don't have any control over or knowledge of what those concrete lifetimes will be.
We're only able to say when there are concrete lifetimes,
they will have this relationship.
So, now, let's talk about what we mean by lifetime parameters not being prescriptive.
Lifetime parameters don't prescribe, by which we mean they don't order code to behave a certain way.
They don't specify how long a concrete lifetime of a value or a reference must be.
They don't change the concrete lifetimes of values or references.
Lifetime parameters aren't setting lifetimes,
assigning lifetimes, or forcing lifetimes to be a certain length.
And adding lifetime parameters won't make a reference live long enough to be valid for that code.
Let's take another look
at an example
from the module on visualizing lifetimes to understand borrow checker errors, where we tried to return,
a reference from the return_first_two function.
The reference being returned points to the list vector created within the function. When we looked at the lifetimes
previously,
we saw list goes out of scope and gets cleaned up at the end of the
return_first_two function.
The return reference is invalid because it outlives the value it's pointing to.
When we compiled this code,
we get an error that says,
missinglifetime specifier. Now that we've covered what lifetime specifiers are, let's try adding a lifetime specifier to see what happens.
The error message we get with Rust 1.24.1 says that the borrowed value does not live long enough.
Adding the lifetime specifier has not changed the list value's lifetime to make it live long enough for us to return a reference to it out of the function.
This error message has been improved in newer versions of Rust. With Rust 1.35, the compiler says that we cannot return a value referencing local variable list in the return value reference's data owned by the current function.
This error message is more specific about the root cause of the problem.
Next,
let's go back to the error message we got when we didn't have any lifetime parameters.
The error message had some help
text that said
consider giving it a 'static lifetime.
Before we try that,
let's talk about what the 'static lifetime even is.
The 'static lifetime is a concrete lifetime -
the only concrete lifetime you can specify in code. The 'static lifetime is for references that are valid the entire time your program is running.
It's named static because it's similar to static variables,
which are also valid for the entire program.
You can get a reference with the 'static lifetime by taking a reference to static variables,
constants, or literals.
Let's return to our code and try adding the 'static lifetime to the return reference.
We don't need to declare it because 'static is built into Rust.
This gives us the same borrowed value
does not live long enough error we got when we used 'a. Again,
because lifetimes don't force values or references to live longer,
adding 'static doesn't make list live for the entire runtime of the program.
Using the 'static lifetime would work if the value we reference is,
for example,
a static array instead of a vector.
This code compiles and runs successfully because the static variable is valid anywhere in the program,
so a reference to the static variable is also valid anywhere in the program.
Rust suggests that we use the 'static lifetime in the return_first_two function because it's actually the only lifetime that could work for a function returning a reference that has no references in its parameters.
There's nothing for the return reference to be related to,
so it must be referencing a value that exists at any time in the program.
Finally,
let's talk about some questions that will hopefully help you fix errors having to do with lifetime parameters.
When you see the error message, "Borrowed value does not live long enough,"
it's natural to
ask, "How can I make this borrow live long enough?" and try to use lifetime parameters to do that. As we've discussed,
that's not what lifetime parameters do.
No amount of adding or rearranging lifetime parameters will extend any concrete lifetimes of values.
Here are some more useful questions to ask yourself in this situation.
Look at the definition containing lifetime parameters.
What are the actual reference relationships in the implementation
you're writing?
And do the generic parameters accurately communicate that relationship to the compiler?
Next,
look at where you use that definition.
What are the concrete lifetimes? Where might there be an invalid
reference because of a value getting cleaned up?
In other words,
where is the use-after-free problem the compiler is preventing?
Then, think about how to rearrange the definition,
the lifetime parameters, or the usage, so that they're all consistent with each other.
And if you're still stuck,
consider if references are right for your case.
Some lifetime problems happen because there's data without an owner,
so using references will never work.
Other times,
cloning data can get the code working,
and then you can profile to see if there are performance problems that justify figuring out how to use references.
In this module, we discussed that lifetime parameters describe relationships between references.
Lifetime parameters don't prescribe how long values or references live.
The 'static lifetime is for references that will be valid at any point in the program.
But specifying 'static won't make a value live that long if it doesn't already.
Given these properties of lifetime parameters, we talked about questions that will hopefully help next time you're getting errors about lifetimes.
Next,
we'll cover lifetime elision.