[No Audio]
In the previous tutorial, we have started our
discussion of the threads, and we learned
about the basics. In this tutorial, we will
learn multiple threads and see concepts
related to the issue of ownership. Let's
start with an example. I haven't first bring
the relevant modules into scope.
[No Audio]
I will next create a vector of threads.
[No Audio]
The type will be inferred by the Rust compiler
when I assign some values to the vector.
Next, I want to create 10 threads all
containing the same code. So I will include a
for loop which will iterate 10 times.
[No Audio]
Inside the loop, I will push a newly created
thread inside the vector.
[No Audio]
Next, I will spawn a thread, and we'll include
a simple print statement inside the body of the thread.
[No Audio]
The compiler is not happy in this case, it
says closure may outlive the current
function, but it borrows i, which is owned by
the current function, and may out live to
borrowed value i. Now let us see what does it
mean. If we inspect the code, we may note
that the variable i which is used with the
for loop is part of the main thread, and is
not part of the thread that we are creating.
This means that the variable is not, is not
part of the thread. Moreover, the closure
inside the thread takes no arguments, which
means that we are not using any data from the
main thread in the spawn threads code. To
use data from the main thread in the spawn
thread, the spawn thread closure must capture
the values it needs. Since we are not sure
that the thread will live long enough as the
main, therefore, to avoid possible lifetime
issues, the Rust demands that the value may
be moved inside the closure which resides
inside the thread. This can be done with the
help of the move keyword, let me add it.
[No Audio]
This means now that the individual value of i in
each iteration of the loop will be
transferred to inside the thread, and now the
thread is is basically in charge of these
values. Let us execute the code now a few times.
[No Audio]
You may note that it is not necessity,
necessity that all the threads are being
created. This is because some of the of the
threads did not get a chance, to make sure
that all the threads get a chance to execute,
we will use the joint function in the main.
Since there are 10 threads, so I will call the
joint for each of the threads. I will include
a for loop which will iterate as many times
as there are threads in the thread_vec.
[No Audio]
During each iteration, I will call the join
on the variable of i, which is assuming
values from the thread_vec, which
contains the threads that we have created
using the code above.
[No Audio]
Let's execute the code a few times.
[No Audio]
You may note that each time all the threads
are created and finished. However, it is not
necessity that they will be created in the
exact same order. The summary is that the
move keyword is used to take ownership of the
values that the closure uses from its
environment, and we will need it in case of
threads, so that they are able to take
ownership of the values from the environment.
Let us cover some more concepts,
but first I will comment out the code.
[No Audio]
Let me define a vector first.
[No Audio]
Next I will create a thread and we'll assign
it to the variable.
[No Audio]
Inside the thread I will
use the variable v inside the print statement,
so I will add a print statement, and we'll
make sure that I move the variable v inside.
[No Audio]
Outside the thread, I will first call join on
the threads to make sure that it gets a
chance of being executed.
[No Audio]
This works fine.
But now let me call a drop on the
variable v after the thread.
[No Audio]
There is an error message
you may have already guessed
the error but let us look at the details, it
says, use of moved value v. It further
says, move occurs because we have typed vec i32.
Which does not implement a copy trait.
The error in this case is that if the thread
is scheduled to be executed after this line
in the main, then there will be potential
issues because the thread is using the
variable v which does not exist. The Rust
therefore rightly complain. Okay, what if I
initialize a primitive variable which has a
copy trait and then I drop it, I will define
a variable x and we'll initialize it from
some suitable value and then I will use it
inside the print statement included in the thread.
[No Audio]
Finally, I will pass it to the drop
function. Now, you may notice that this code
is perfectly fine. The reason behind this is
that the drop does not work for the copy
types. This means that the drop does not add
the scope of a primitive variable. Therefore,
the variable x remains there and there are no
errors. Now some of you may be asking that
if that drop cannot work with the primitive
types, then how we can drop a primitive
variable. The only meaningful way you can do
it is to let the primitive variable fall out
of scope in which it is defined. For
instance, if I enclose the variable x inside
brackets, then it will reside inside this and
therefore it is not available to the thread.
The final point to note is that the move is
required for both the primitive and
non-primitive types in the closure to thread.
However, the primitive variables are being
copied, while the non-primitive types are not
copied, but their ownership is rather being
transferred. To see this I will add a couple
of print statements after the thread
[No Audio]
You may note that, the variable x has no
issues, but the variable v has some errors.
In summary, the move with the closure has
different effects for both the primitives and
non primitive variables. Okay with this we
end this tutorial. See you again and until
next tutoriall, happy Rust programming.
[No Audio]