Welcome to module
8 on
lifetime elision. In this module,
we'll define what lifetime elision means.
We'll go through the three rules the compiler uses to figure out what any alighted lifetimes are.
And we'll review some examples from previous modules that involve lifetime elision,
which we glossed over at the time.
Let's get started by talking about what lifetime elision is.
Elision is a relatively uncommon English word that means omitting or leaving out.
Lifetime elision is a concept unique to Rust.
It's a set of rules programmed into the compiler that were added so that you don't have to add generic lifetime parameters on every reference.
The people who worked on Rust noticed that there were many common patterns for using lifetimes.
They coded rules into the compiler to apply default generic lifetime parameters to use
in these cases.
These rules only take into account the signatures of functions and methods.
The bodies don't have any influence over lifetime elision.
If,
after applying the rules,
Rust still can't figure out the lifetime parameters for all of the references in the signature,
you'll get a compiler error that says you need to add lifetime parameters.
If you want to do something other than the defaults, you can add lifetime parameters to specify a different relationship.
If you want to annotate lifetimes,
even in cases where lifetime elision would let you leave them out,
feel free, even though this isn't idiomatic Rust.
Next,
let's look at what the three lifetime elision rules are.
The first rule says that each parameter that's a reference is considered to have its own lifetime parameter.
For example,
if we enter this function that has three parameters,
the compiler interprets the first two parameters that are references as having their own lifetime annotations, shown here as 'a and 'b. The second rule says that if there's only one lifetime in the parameters,
a return reference gets that lifetime.
For example,
if we enter this function that has one reference parameter,
the first rule says that the reference gets its own lifetime,
and the second rule says the return reference gets that lifetime as well.
This is because any other association would be invalid unless the return type's lifetime is 'static,
as we discussed in the previous module,
which we would need to add manually.
The third rule only applies to methods. It gives return types the same lifetime as the self parameter,
even if there are other parameters with different lifetimes.
For example,
in this get_value method implemented on a Config struct,
the compiler would use the first rule to assign different lifetimes to the self and key parameters.
Then, the third rule tells the compiler to assume the return string slice has the same lifetime itself,
which would be 'a here.
This is because it's very common for methods to return
a reference to a part of self.
Now that we've covered all three rules,
let's re-examine the three examples that demonstrated the rules to see if those examples would compile.
In the first example we looked at,
neither the second nor the third rules apply.
The return reference still doesn't have a lifetime,
so we'd get a compiler error saying we need to add lifetime parameters.
In the second example,
after applying the second rule,
all references in the signature have a lifetime.
So, this example would compile successfully as long as the body aligns with the assumed relationship.
Similarly,
the third example also has lifetimes for all the references and compilation can continue.
Finally,
let's look at some examples from previous modules to see how lifetime elision affects them.
Way back in the module on concrete lifetimes, we introduced this example with the return_first_two function that has a reference as a parameter and returns a reference. In the module introducing generic lifetimes,
we said that the return_first_two has a generic lifetime,
which we represented with an orange dotted line.
We didn't look at this example in the module where we learned the syntax for adding generic lifetime parameters because it doesn't need any. Because of the second lifetime elision rule,
the return reference gets the same lifetime as the one parameter and this code compiles.
In the module on generic lifetime parameters,
we looked at an example with the Stemmer
struct that has this stem method.
When we compiled this without any lifetimes,
we got an error
that said,
this parameter and the return type are declared with different lifetimes....
You may have been wondering why the compiler said that,
seeing as how the method doesn't appear to contain any declared lifetimes.
Lifetime elision is why the compiler thinks that there are lifetimes here.
The first rule gives self and word separate lifetimes.
And the third rule assumes that the lifetime of the return type was the same as the lifetime of self - 'a here.
When the compiler proceeded to verifying that the references were valid according to the specified lifetime relationships,
it sees that the implementation doesn't align with the lifetimes it assumes from elision
and we get an error.
We fixed this error by associating the return reference's lifetime with the word parameter instead. And, again, because of lifetime elision,
we didn't need to add a lifetime for the self parameter.
The first elision rule will assume it's a different lifetime than word.
So, when we fixed this example earlier,
we only specified one lifetime parameter. In this module,
we discussed how lifetime elision is a feature that lets us leave out generic lifetime parameters
in common cases.
We covered the three rules the compiler uses to figure out lifetimes when they're omitted.
Each reference parameter gets its own lifetime.
If there's one parameter lifetime,
a return reference gets that lifetime, and with methods,
a return reference gets the lifetime of the self parameter.
And we looked at some cases where lifetime elision lets us leave some generic lifetime parameters out, in the return_first_two function and the stem method.
Congratulations!
You've made it to the end of the Rust in Motion video
course.
Now would be a great time to write some Rust code by doing some programming practice problems like rustlings
or exercism, or working on a project like writing a command-line utility. Happy
Rusting!