>> Ownership is one of
the most unique and critical aspects of Rust to understand.
This video will take you through concepts of memory management and
how Rusting guarantee memory safety
without requiring the use of a garbage collector.
Ownership has to do with how your program manages memory.
When you run a program in Rust
or in most other major programming languages,
it stores data in
two differently structured parts of your computer's memory.
These structures are called the stack and the heap.
The stack, as its name implies,
is a stack data structure.
The way data moves through it is last-in, first-out.
Let's look at an illustration of how this works.
Let's add data to this stack,
and after we've added the data,
if we wanted to take data off the stack,
we would take that same piece of data we
just added off of the stack.
The stack is where Rust stores data with a known fixed size.
If we were to declare a 32-bit integer and our program,
and then set it to the value of five.
A 32-bit integer has a known fixed size at compile time.
We know it will take
up to 32 bits of space when we run the program.
Because the size of the value is known at compile time,
both the variable and the value of
the variable would be stored on the stack.
However, what about a datatype that can change size at run-time?
Let's consider a vector.
Notice that this vector is mutable.
That means this vector can change
size while the program is running.
We could add another value to the vector and then print
out the value of the vector and when we run this code,
we see these four values.
Because the size of the vector can change.
This time we store data in both the stack and the heap.
On the stack we store a representation of the vector variable
itself and a pointer to a memory address on the heap.
The heap is where we store the actual content of the vector.
If we were to drop this vector using Rust drop method,
both the data on the stack and
the heap would no longer be accessible.
So far, the concepts we've discussed are not unique to Rust.
Most programming languages store
data on both the stack and the heap.
Where they differ is how they manage that memory,
especially when values are dropped or go out of scope.
Let's use a different data type this time.
A string also stores data on both the stack and the heap.
Notice that this string is mutable.
That means it can change while the program is running.
This means the string needs to be stored
on both the stack and the heap.
We could add contents of the string like this,
and then print the string.
When we run this program,
we see the full modified string value printed out.
Now, let's consider what would happen if we were to create
a new string and assign it the value of our original string.
In this case, we create a copy of the data stored on the stack.
We would take the representation
of our original string on the stack,
including the pointer to the heap,
copy it, and assign it to the new string variable.
This means our second string would have
the same pointer as the first string.
While we do copy what is stored on the stack,
we do not copy what is stored on the heap.
The pointer associated with the second string would point to
the exact same address as
the pointer associated with the first string.
This introduces a new question.
What if we were to delete or drop the first string?
It seems like both the data stored on the stack and
the heap for the first variable would no longer be valid.
Does that mean the second string would
still point to the same place on the heap,
even if the value on the heap is no longer valid.
Rust has rules to prevent this situation.
But before we go into them,
let's briefly cover how
other programming languages have handled this problem.
This scenario, we have a pointer on the stack pointing to data
on the heap that is no longer valid
is known as a dangling pointer.
This can cause your program to crash
and can be a major security issue.
Until Rust came along,
there were two main approaches to preventing issues like this.
Languages like C and C++ depend on
the programmer to explicitly manage the memory used by their code.
That means it is your responsibility to
allocate or deallocate memory as your program needs it.
This may sound straightforward in theory,
but as code-bases grow and become more complex,
manually managing memory becomes much more difficult in practice.
The other approach that some programming languages
have taken is to use a garbage collector.
If you have used a language like Python, Ruby, or JavaScript.
You have used a garbage collected language.
The garbage collector is a program
that runs while your program runs.
Its job is to constantly look for memory that is no longer
used and clean up any references to that memory.
This works well for many languages,
but using a garbage collector adds overhead,
which makes these languages less
suitable for low level systems programming.
Rust takes a third approach to memory management called ownership.
Ownership consists of a set of rules that
the Rust compiler verifies at compile time.
The first role is each value
in Rust has a variable that is called its owner.
If we were to look back at this line of code,
the first variable is the owner of this string.
Now, let's add this line of code which assigns the value
of our second variable to be the value of our first variable.
This brings us to the second rule of ownership.
There can only be one owner of a value at a time.
Both these variables cannot be owners
of the same value at the same time.
We need to completely move ownership to
the second variable and then invalidate the first variable.
If you are familiar with C++,
this might sound like making a shallow copy,
but there's a key difference.
Rust doesn't just copy the data in the stack.
It then invalidates the variable we move the data from.
Once the value that used to be associated with
the first variable is moved to the second variable.
The first variable is no longer
considered valid by our Rust program.
If we were to try to access
the first variable after its value is moved,
and then try to compile the code,
we would receive an error.
This rule means we don't need to worry about dangling pointers.
Only one variable can be pointing to
the data on the heap at any given time.
This prevents a lot of memory safety issues,
including dangling pointers, double free,
which involves trying to free memory that has already been freed,
and memory leaks which involve
not free memory that should have been freed.
This has also helped by the third rule of ownership.
When the owner goes out of scope,
the value will be dropped.
To understand this rule,
let's go over what we mean by scope in the context of Rust.
A variable scope is the range within a program for which
that variable and the value of the variable are valid.
We'll discuss that more in the next video.
This gives you a high level overview of ownership in Rust.
In the next video, we will examine how these rules
apply to values that are passed between functions.
I'll see you there.
[MUSIC]