[No Audio]
Lifetimes are one of the most important yet
probably one of the least understood topics
in the Rust programming language. The
Lifetimes explains the scope for which
reference is being valid. Rust claims to be
memory safe, and we have talked about that to
some extent under the topic of ownership in Rust.
The concept of Lifetimes in Rust is
also part of memory safety feature. This will
become more clear to us in this tutorial
after covering what Lifetimes are, why do
they exist, and when do we need them?
So let's start learning about Lifetimes. Before
starting, we will address the question
of why we need Lifetimes.
In fact, it will be better to highlight at this point that,
in this particular tutorial we will be first
explaining some of the problems that sets the
motivation for the use of Lifetime specifier.
And then in the next tutorial, we will be
looking at the possible solution.
Okay, let us come back to the basic question of
why we need Lifetimes? To better answer this
question, we will need to understand what are
dangling references.
When the references are
being used to point to some variable or
resource in memory, so there is always an
owner of that resource or variable.
The dangling reference is a reference which
points to a resource, whose owner does not
exist. In other words, when a program tries
to access an invalid reference, it is known
as a dangling reference. By invalid, we mean
that the resource has been deallocated.
Dangling reference is also sometimes called
as a dangling pointer. Let us illustrate this
with the help of an example. I will declare a
variable which will hold a reference to an integer.
[No Audio]
Next, I will include a code segment.
Inside the code segment I will declare a variable j,
and we'll set its value.
[No Audio]
I will next set the
variable i as a reference to the variable j.
Outside the code segment, I will access the
value of the variable i by printing its value.
[No Audio]
You may note that the compiler is
complaining, it says j does not live long enough.
Some of you might have already
guessed what the problem is, but let us explain this.
When we were explaining the
ownership rules, we explained the concept of scope.
The scope of a variable is the code
segment in which it is defined. In
this program, the scope of the variable i
start from the first line of the program, and
ends at the last statement of the program at
this particular closing curly brackets.
The variable j comes into scope inside the code
segment at this line in which it is defined.
The variable j goes out of scope when the
ending curly brackets are encountered for the
code segment. Outside the code segment, since
the variable i is pointing or or referencing to
a resource which does not exist, therefore
the compiler is complaining. The variable i,
in this case, is said to be a dangling
reference, or a reference which is pointing to
an invalid resource. The definition of
Lifetime is now very easy to understand and
comprehend. The Lifetime explains the scope
for which a reference is valid. For instance,
the Lifetime of the variable j in the
above example starts from the line in which
we initialize it, which is this particular
line of code, and ends at ending curly
brackets when the code segment ends.
The Lifetime of all the variables end at the
ending curly brackets where the main function
ends. In summary, some variables may have
longer Lifetimes and other may have shorter
Lifetimes. A reference variable must live
long enough and should have a longer lifetime
enough for the duration in which it is being referenced.
Dangling references are typically
encountered, when we are dealing with
functions. Let us see this using some suitable examples.
I would start by declaring a variable.
[No Audio]
I will define a function called some_fn.
This function will accept an integer input
and will return a reference to an integer.
[No Audio]
Inside the body of the function, I will
return a reference to the input variable i.
I will use the same function in the main
program and will assign it to a variable.
[No Audio]
Finally, I will, I will use a print statement
for printing the value of additional_int variable.
[No Audio]
You may note that the compiler has already some
useful information for us, it says missing
lifetime specifier. We will explain the
lifetime specifiers later on, but for now let
us see what has gone wrong in this case. In
the main program we have defined a variable
of some_int, which is the owner of the value.
In the second line of the code, we
are calling the function with a variable of
some_int. Since we are using the
value and not the reference, so when the
function is called and executed, it will take
the ownership of the value. And now the owner
is the variable i and not the variable of some_int.
Inside the function, we
are returning a reference to the variable i.
Now have when the function ends, the variable
i will be no more in scope, and in other
words, its lifetime will end. So when we
return back to the main function, that is
that is to the second line of the code in
the main function, the variable i will no
more exist, because it was defined inside the
function and its scope was limited to the
body of the function, and is no more in scope.
Therefore the variable of additional_int
will be pointing to or
referring to a resource which is invalid and
does not exist. For this reason, it is known
as a dangling reference.
In this case, the variable i does not live long
enough and its lifetime was being limited to the function.
The solution in this case would be to use a
reference in the function instead of the
actual value. So let me use a reference in
the function call, and also in the function definition.
And now you may note that, the
compiler is no more complaining.
Before moving further, it is important to highlight
it the Rust has a special module called the
Borrow checker, which checks for the lifetime
related issues. It is tasked with enforcing a
number of properties related to the ownership and lifetime.
In addition to dangling pointers,
there is another issue or problem
in connection with the lifetime called
undetermined lifetimes. Let us learn about that also.
The Rust compiler checks for the
lifetimes related issue as issues at compile time.
Sometimes it may happen that at compile
time, the Llifetime of a variable may not be known.
Let us see how this can happen.
I will define a couple of variables.
[No Audio]
Next I will write a function called greater,
which is going to return a reference to the
greater of the two values.
[No Audio]
Inside the body of the function, I will
compare the two values and will return the
greater, let me add the code.
[No Audio]
In this case now, a reference to a value which
is greater will be returned from the
function. Please note that in this case, we
do not have a dangling pointer or reference,
because we are passing the value to the
function by references, which means that the
ownership of the values does not change, and
therefore the two variables in the main
function are valid. However, the Rust
compiler is still complaining, it says missing
lifetime specifier. The reason for this is
that the Rust is confused in this case, as to
which Lifetime the returning reference may be
corresponding to, the actual reference cannot
be determined at compile time and can only be
determined during the execution time,
Because it depends on the if statement condition.
This scenario is referred to as undetermined
lifetime case. Let us look at another example
of the same issue to better understand the concepts.
I will use strings in this example
now, I will declare a string variable with the
value of Hello.
[No Audio]
Next inside the code segment,
I will declare another string variable with
the value of World.
[No Audio]
I will include a function now, which will take
two string slices as an input, and will return
the first slice as an output.
[No Audio]
Inside the body of the function, we will write first_str.
[No Audio]
In the code segment, I will call the function and we'll
assign it to some variable.
[No Audio]
I will declare the same variable v before the
start of the code segment. Outside the code
segment, I will use the variable v in a print statement.
[No Audio]
You may note that the compiler has issues, again
please note that we do not have dangling
pointers in this case. The variable v is
pointing to the variable s1, which has the
scope of the entire main function. Let us inspect the error.
It says expected named lifetime parameter.
Moreover, it says that
this function returns type, contains a
borrowed value, but the signature does not
say whether it is borrowed from first_str
or second_str,
this leads to the issue of undetermined lifetime.
The compiler needs to explicitly
know in this case to, which one of the two
variables the returning variable should
correspond to, as it is unable to reduce it
at its own in this case.
Okay, that brings us
to the end of this tutorial, we have covered
two important issues called a dangling
pointers or references and undetermined
lifetimes. In the next tutorial, we will be
looking at the concept of Lifetime specifier,
which will enable us to design solutions for
these issues. Do come back to cover that and
until next tutorial, happy Rust programming.
[No Audio]