- In this section,
we are going to discuss copying
and moving in Rust,
a concept that most languages don't have.
So there are concepts that we need to learn
and syntax, well,
the syntax is actually quite straightforward.
It's the concept that's the tricky part.
It's all about what happens
when you assign one variable to another.
You'd think it would be straightforward.
Take the value of B,
copy the value into A,
what could possibly be difficult about that?
It turns out to be quite tricky actually.
What happens, it all centers around
a trait called copy.
So if you remember in Rust,
a trait is like an interface in other languages,
and there's a special predefined trait
called copy, which determines how values
get assigned and copied,
when you have an assignment
like we've got here.
So, this copy trait,
if you have a data type which implements copy,
it means that you can copy
from one variable to another
just by doing a bit copy.
You can just copy the bits
from one value into another.
Right, so for simple types,
like integers and floats
and bullions, et cetera,
they do implement the copy trait,
and that means when you assign
one variable to another,
it just does a bit copy,
and both variables end up having the same value.
Okay?
So, if B was 42 and I said,
so that's that,
and then I said A equals B.
Then A would just be a bit copy, 42.
Okay, so it'll copy from B into A
just by doing a bit copy.
They both end up being 42.
That's quite straightforward.
So you know, if that was the end of the story,
that would be the end of the video.
But most types in Rust don't implement copy.
Okay?
A good example of that is a string.
We'll have a look at an example
of this in a moment.
So imagine I've got a string, B,
and it holds this text.
Hello.
Like that.
So B basically holds a pointer
towards the address of that text.
That's basically what happens.
Now the string structure doesn't implement copy.
So if you said A equals B,
all it will do,
well, It still does a bit copy,
but it does a bit copy of the pointer.
It's like a shallow copy, like that.
So it'll just copy the pointer
into A, if that's A, yeah.
So A also ends up pointing to the same place.
What it also does though
is it basically unhinges object B.
Okay?
It automatically does that.
When you assign for types
that don't implement copy,
when you assign one to another,
it transfers ownership from that object
into that object,
and you can't use the original object afterwards.
You can't use B,
because it no longer owns the text.
You have moved ownership.
So for types that don't implement copy,
like string, the value of B is moved into A.
The value that B used to have
is now effectively owned by A.
A has acquired ownership and B has lost ownership,
and you can't use B afterwards.
If you try to, you get a compiler error.
So let's have a look at
simple types that do implement copy,
like integers,
and then types that don't implement copy,
like strings.
So here's an example with integers.
Int, 32.
They all implement the copy interface.
So, a copy trait.
So here, A is originally this bit pattern,
and then I assign A into B.
That's a simple bit copy,
because I32 implements the copy trait.
So just do a simple bit copy,
A and B are both 42,
nothing much to see here.
So the assignment copies the value
from A into B, as we've seen.
You can use A afterwards,
it hasn't lost ownership,
it's just effectively cloned itself.
So you can use A and B quite happily
after that event.
Simple.
Right, moving a value,
for types which don't implement copy,
like string.
Have a look what we've got here.
So S1 is a string object,
which means that, you know,
basically it holds a pointer to some text.
Hello.
Then we have an assignment.
This assignment, S2,
is gonna be here and all it does,
it doesn't do any kind of cloning,
because that would be inefficient.
It just basically copies
whatever's in there into here.
Okay?
So, basically whatever pointer value
used to be in here,
that pointer value is also here.
So S2 points there,
but S1 has now lost ownership.
Okay?
So ownership of the text has moved,
S1 has lost ownership,
and S2 has acquired ownership.
So, S2 will be hello,
but you can't use S1.
Okay?
S1 has lost ownership.
Ownership has been moved into S2.
You can't use S1 afterwards,
and that's actually enforced by the compiler.
If you do try to use S1,
you'll get a compiler error.
It'll say you've lost ownership.
Ownership has been moved away to S2.
A move occurs when you move ownership
of the data from S1 into S2.
You can't then use S1 afterwards.
So the assignment operator moves the value
from S1 into S2.
The pointer is copied into here.
So effectively,
ownership of that text is now owned by S2,
not by S1.
And you can't use S1 afterwards.
Right, well let's see the example in our demo,
project lesson six, scope ownership.
Demo copying versus moving.
Let's have a look at that,
and then we'll run it.
Okay, so first of all, in main,
let's uncomment the bit of the demo
that we want to cover.
Demo copying versus moving.
And it's here.
So, for simple types,
A is 42, B will also be 42,
it'll print 42.
42.
Okay, good.
For types which don't implement
the copy trait, like strings,
S1 is currently hello,
but I move ownership,
so that S2 now owns the text.
S2 is hello and S1,
I can't use it afterwards.
If I tried to use S1,
I'd get a compiler error,
because I've donated,
I've moved ownership to S2.
S2 now owns the text.
I can use S2 obviously,
but I can't use S1.
Let's see what would happen
if I did try to,
let's uncomment that line,
and see what compiler error we would get.
Okay, so I'm just gonna do a cargo check,
just to check the syntax.
This is a compile time enforced rule,
enforced by the compiler.
Right, I did say earlier
that the error messages
that you get in Rust
are actually very helpful, quite verbose,
but they tell you what led up to the error,
and then what the error is.
So, on line 14,
I declared my string.
S1 is a string.
And so, S1 has type string,
which does not implement the copy trait.
It's very chatty, isn't it?
So therefore, a move occurred here.
Okay, so basically,
S2 ownership is now moved into S2.
So S2 basically owns the text,
and S1 doesn't.
So basically,
without getting too much into
the actual technicalities
of the error message here,
it basically means you can't use
S1 afterwards,
because its value has been lost,
moved into S2.
So you can't use a variable
after it's been moved away.
The reason why Rust does this,
is to simplify when it knows clearly,
when to deallocate the text for the string.
Rather than having string one
and string two both kind of point
into the same buffer.
You only have one active owner
of memory at a time.
So therefore,
when S2 is deallocated,
its distractor, its drop function
will deallocate the text.
Effectively, when you move ownership
away from S1,
it's as if that variable
had never existed, okay?
It's gone now.
S2 is now the owner and is therefore
responsible for deallocating the text
when S2 goes outta scope.
Right, so if I run the application now,
it should work okay.
I'm not trying to use S1 anymore,
because I can't,
because I've moved ownership.
I can use S2 though, that should be fine.
So let's do a cargo run just to confirm.
So A and B, both printouts being 42,
and S2, printout is being hello,
and goodbye from this demo.