[No Audio]
In this tutorial, we will be covering some
additional topics with regards to closures.
More specifically, we will be exploring some
concepts related to closures and its
capturing environment. So let's get started.
Let us first refresh the contents from the
last tutorial. In the previous tutorial, we
looked at different syntaxes for defining the
closures, let me quickly look at some of
these. The first syntax was to explicitly
define the inputs and outputs.
[No Audio]
The second syntax is to skip the types of
the inputs and outputs.
[No Audio]
The types will be
inferred in this case, when the closure is
called for the first time in the program, and
if the compiler is unable to infer, then it
will complain. The third syntax was, when we
have only a single statement in the body of the closure.
[No Audio]
In this case, we do not add the curly
brackets. These three are basically three
different styles in which we may define the
closures. Please note that the compiler is
complaining for the some_closure_2,
and this will be resolved, when
we use the closure for the first time inside
the program with some suitable inputs, because
the compiler is unable to currently deduce
the inputs and output types. Okay, that was a
quick recap, and now we will cover the main
topic of this tutorial, which is related to
closures and it's capturing environment.
We will first look at the case, when the closure
uses an immutable reference to a variable
inside the code. I will declare a mutable
vector with some values. Next, I will define
a closure, and we'll use this vector inside
the closure in a print statement.
[No Audio]
Let us now try to access the same vector
outside the closure in a print statement.
[No Audio]
Finally, I will invoke the closure.
[No Audio]
Let us now understand what happened in this case.
The first thing to note is that, we do
not have mentioned the input to this closure.
However, since the closure captures its
environment, so all the variables which are
currently in scope will be available inside
the closure also. Moreover, in this case the
Rust compiler will also decide how to use the
variable inside the closure, as it is not
being specified in the inputs and outputs of
the closure. This means that the Rust
compiler will be engaged in some inference,
since the value is not being updated inside
the closure. So by default, the Rust compiler
will assume that, the value will be used
inside the closure as an immutable reference,
and therefore it will not change. Since the
value inside the closure is being used as an
immutable reference, therefore vec_1
is available for us, as it is
being used by reference. Remember the Rust
ownership rules, according to which multiple
readings of the values of variables or
reference to the value of variables are okay,
and does not create any issues. Therefore,
the print statement outside the body of the
closure is not creating any issues. Now if I
add a statement of, let's say vec and then
within the [1] = 15,
the Rust compiler will rightfully complain.
This is because, the vector is being borrowed
is immutable and we cannot mutate or change
its value, until the immutable reference goes
out of scope. Once the some_closure
is done, then the immutable reference will be
out of scope. And then we may update its
value. If I get the statement of vec_1 = 15
from here and paste it after the
statement of some_closure, so the
compiler will have no issues.
Okay, here is a small point to note the closure as pointed
out in the previous tutorial are a bit
different from a that of typical functions.
Typical functions will only get a chance of
being executed when they are being called
from the main function or some other
function. However, as soon as we declare the
closure and assign it to some variable, so
since the variable is in scope, the
definition of the closure which is tied to
the variable is also in some sense in scope.
As a result you may note that, although we
are calling the closure on this line, the
vector of vec_1 is still being
considered to be borrowed until the line,
where we are calling the closure of some_closure.
After the call is completed the immutable
reference ends. This
is why we can then modify or update the value
of vec_1. Okay, now let us see
what happens when a closure uses a mutable
reference to some variable. I will change
the statement inside the closure, so I will
delete the statement and will push a value
of 35 to the vector.
[No Audio]
Outside the closure I
will keep the statement where I am calling
the closure and will delete the rest of the
statements. In this case now the closure is
modifying the variable. So, the Rust compiler
will infer, that the variable will be borrowed
by the closure is a mutable reference, since
it is a mutable reference. So therefore, an
immutable reference to the same variable that
is vec_1 will not be allowed until the
variable of some_closure is in
scope. Moreover, the variable of some_closure
will be in scope until it
is used for the last time. Moreover, please
also note that we need to write the mut
keyword before the variable of some_closure
because in this case, the
closure it is representing we'll be updating
the value of some variable and therefore it
needs to be mutable. So let me write mut
before the name of the variable some_closure.
Okay, what will happen if
I add a print statement before the call to
the closure, I will add the statement.
[No Audio]
And now, I will give you 10 seconds to think
about the possible answer.
[No Audio]
I believe you would have guessed the correct
answer, it is not allowed because it is being
borrowed as mutable, since accessing the value
is not allowed when a variable is being
borrowed as mutable therefore, the Rust
compiler will complain once the updation is
done then we may proceed with using the value
Okay now, I will take another short quiz, I
will comment out this statement and now I
will try to update the value of vec_1.
[No Audio]
This is also not allowed, but I want
you to tell me the answer. I will give you
again 10 seconds to think.
[No Audio]
I believe you would have guessed the answer.
This is because multiple updations to the
same variable are not allowed. In other
words, we cannot borrow as immutable twice.
This is against the Rust ownership rules.
According to the ownership rules, we can only
have a single immutable reference.
In summary, in this case not only the immutable
but multiple references are also not allowed
when a variable is being used as a closure, unless
we are done with the call to the closure and
the variable representing the closure is
no more in scope. We are however free to do,
whatever we want to do with the vector once
the call is done. That is after the line of
some_closure. This is because the
variable of some_closure is being
dropped after the line in which we call the closure.
We will cover the final topic of
this tutorial. Now, we will be looking at a
case when a value is being moved inside a
closure. I will remove the statement inside
the closure body, I will also keep only the
line containing the call to the closure and
with remove all other lines.
[No Audio]
Inside the body of the closure, I will
declare another vec which will be
initialized from the value of vec_1.
[No Audio]
Outside the body of the closure, I will
add a couple of print statements after the
call to the closure.
[No Audio]
Before explaining the issue in this print statement,
we will first explain what happens inside the
closure. The Rust compiler will try again to
infer, how to use the value of the variable
vec_1 since the line of code
is actually moving its value and transferring
the ownership from from vec_1 to that of vec_2.
Therefore the Rust compiler will use
the original values and not as a reference.
Since the ownership has been transferred
inside the closure, therefore the use of vec_1
inside the print statement is
not valid because the variable vec_1
is no more in scope and has
lost the ownership. Moreover the value of vec_2
is also dropped since its
scope was limited to the body of the closure
so therefore the use of vec_2 inside the
print statement is not valid. Okay with this
we in this tutorial, we have looked at three
scenarios in which the value of a variable
may be used inside the closure. That is by
immutable reference, by mutable reference, and
by actual value. This specific case on how to
use the values are inferred by the Rust
compiler depending on how the closure
operates on the values. Do come back again for
learning more and until then happy Rust programming.
[No Audio]