- Okay, so you can force a closure
to capture a variable by value.
You can forcibly say, move.
You can tell the compiler
no matter what you might think about the closure.
You might think that the closure
can just capture by reference,
but I'm telling you, don't do that.
You must do a move.
You can forcibly tell the compiler
to move ownership into the closure,
and that's useful when you're spawning
another thread.
Okay?
In Rust, one thread can spawn another thread.
You can create another thread.
The thread executes code in a closure,
you give it a closure,
and it executes that code on a separate thread.
Now, the thing is,
the thread, the closure that you've given it
might be long living
and it might outlive the outer function.
So if there were any variables
in the external function,
they must live long enough
for the closure to complete.
And the way you achieve that
is by moving captive variables into the closure.
Okay?
Basically the thread must own those variables
for as long as the thread lives.
It can't just rely on the outer function
living long enough because it could terminate
quite quickly.
So if the thread does need to capture
any variables,
it must do it via a move,
so that the variables live long enough
for the thread to complete.
So the syntax of doing a forceful move
looks like this.
I've got some outer context,
with a message that might have a very short lifetime.
I tell, when I call the spawn method,
the standard crate has a thread module
which has a spawn function.
And the spawn function takes a closure
to say run this code on a separate thread.
So it displays the message,
it then waits five seconds.
So this closure is long lived,
and then it prints the message afterwards.
If I hadn't used the move keyword here,
well it wouldn't compile, first of all.
But if I hadn't put the move keyword here,
conceptually, what could have happened
is that the, let's imagine that the compiler
didn't understand properly.
The compiler might just say,
"Oh, all he's simply doing
is printing the message.
So maybe what I'll do,
is I'll just have a reference
to the external variable up there like that."
The problem being, of course,
that this closure is gonna take five seconds
to complete, and within that five seconds,
this external function could have returned,
in which case that message
doesn't exist anymore.
And then we've got a problem,
because five seconds later,
we're gonna try to access the message again
and it's gone.
So in that case, if you have spawn in the thread,
is the classic example.
If the lambda or the closure
doesn't apparently need to do a move,
then you have to forcibly do a move.
Okay?
Basically what will happen is,
you will move this object
into ownership of the closure,
so that that object will then live.
It'll belong to the closure
and will live as long as the closure,
it doesn't disappear halfway through.
So this is called forcible movement.
Let's see an example of that.
So capturing a value forcibly.
Here's my external string
and this is the start of my method.
And by the way,
there's the end of my method.
And here I run, I spawn another thread.
We're gonna look at multi-threading
in more detail later on
in the video series,
but this was kind of relevant here.
We must forcibly move any variables
that we mention here, like message.
We must forcibly move them into the closure,
otherwise it won't compile.
So the closure object will basically
seize ownership of the message,
and that message will live as long
as the closure lives,
which is gonna be five seconds.
Okay?
So it'll print "Hello", and there it is.
It'll wait five seconds,
and then it'll print "Hello" again.
And let's see it in practice.
I just wanna make sure
that I've called that function from the top.
So from my top level function,
capture value forcibly.
Oh, by the way,
notice my ten second delay to give the example
enough time to run through the completion.
So let's see what gets printed.
I'm just gonna wait for the example,
you can see, there's a weight going on here.
And this, this is the ten second wait,
it's finishing.
So it said start of method
and start the method.
And then this thread got spawned.
And when you spawn a thread,
it takes, you know,
it takes a few milliseconds to get going.
So before it could actually print anything.
The main thread, there were two threads running now.
The main thread and the thread
that's running the closure.
The main thread continued and said end of method.
So this function has actually returned
back up above.
Okay, so the main thread
is now basically waiting
on a ten second wait.
So this function is already terminated,
and if this message had just been stack based,
we'd have lost it,
which is a shame because we needed
to access it here.
So as soon as this closure started,
it immediately moved this message into the closure.
So it lived long enough.
This closure, which is really
an object in disguise now owns the message.
Ownership has been moved into the closure,
which means we can print it
Start with message closure, hello.
And then it waited five seconds,
and then message at the end of closure.
Hello.
So when you have a closure that really,
well, to put it another way,
when you're spawning a thread
that accesses external variables,
you must say move.
You must tell the compiler,
you must advise the compiler
that a movement of state is necessary.
Interestingly, if you forgot to say move,
you'd actually get a compiler error, okay?
Which may be not what you expected.
Let's do a cargo check or cargo run.
So the error message, oof,
it's quite lengthy.
It's saying my thread,
which is capturing message,
the printing macro only needs to borrow a reference.
And it's saying, if you're borrowing a reference,
then the thing that you're referring to,
message, might have disappeared.
The closure might outlive the message.
So it's basically saying you've got to basically
either declare it a static or use the move keyword,
saying you should use the move keyword
to forcibly move ownership of message
into the closure.
So that it will definitely live
as long as the closure lives.
Okay, so I've put the move keyword back in,
could then run the example
and it's working again.
So in this section,
we've discussed how to move a value into a closure.
We've looked at two scenarios.
One where we call the drop function,
where the compiler can see
that the closure requires ownership,
and it will move ownership of the variable
into the closure,
which means you can't then use it afterwards
in the external code.
And the second example we looked at
with spawning threads,
is where we know that we require
a movement of ownership
so that the variables live long enough
for the thread to complete.
In that case,
you have to say move
to make sure the compiler doesn't move
instead of just doing a borrow.