- One of the main goals of Rust as a programming language
and as a library is to simplify memory management
and to avoid some of the errors you can get
in other popular languages
of a similar ilk like C++.
So Rust achieves memory safety via several techniques
such as stack allocation.
When you create an object in Rust,
normally, you allocate it on the stack.
So you avoid all the potential difficulties
of heap-based allocation
where you have to allocate the memory,
remember to delete it, don't try to delete it twice.
So that's much more potential for error in C++,
where objects are allocated on the heap a lot,
whereas in Rust, objects are mainly allocated on the stack,
much simpler, management model for memory.
We have references rather than pointers.
Rust does have pointers,
we'll see that at the end of this lesson,
but in most cases you just use references
and references are more protected than using raw addresses.
Rust also has the borrow checker.
Of course in the compiler,
the borrow checker stops you
from accidentally doing something
like having two multiple references to the same object
which could conflict with each other.
Rust only loves to have one mutable reference at a time.
So the borrow checker helps to ensure the integrity
of the data under the covers.
So that's a saving, in terms of safety.
So generally, avoidance of pointers in your code.
So having said all of that,
there are still some occasions where you do actually
need to allocate something on the heap,
such as allocating a massive amount of memory.
So for example, if you need to allocate 100K,
you can't allocate that on the stack,
it's just not big enough.
So you would have to allocate on the heap in that situation.
Also, if you have a recursive data structure
like a binary tree,
that's difficult to manage without using pointers
and objects on the heap.
So if you do need heap allocation in Rust occasionally,
the way to achieve that
is using something we call a Box.
There's a structure type in Rust called Box,
which is a smart pointer.
Box object, internally, holds a pointer
to something on the heap.
So it's like a, it's a bit like auto pointer in C++,
if you remember that.
So here's an example usage, it's quite straightforward.
When you say Box new,
it'll understand what type of object you've allocating.
And this value here will be allocated on the heap,
and the Box object,
I've got a Box number here,
lives on the stack,
Box number,
and the Box object has a pointer
to the heap-based storage like that.
So the Box object itself lives on the stack.
The memory that you're referring to
lives on the heap like so.
Well, what if you've allocated an object
and you point into it using the Box,
imagine you wanted to print the value out.
Well, you can either use explicit
or implicit D referencing.
So if Box number is effectively a pointer
to this heap-based storage,
containing the value 42,
you can explicitly dereference the Box
to give you the contents of what it points to,
the contents, 42.
That's explicit D referencing,
but println, is a special case,
the println macro also supports implicit dereferencing
without the star.
When you pass a Box object like that into println,
it'll kind of understand what you're getting at.
And again, it will automatically dereference
to give you the contents 42, like that.
But that's a special case.
Println is a special case in that respect.
Most cases, if you wanna access the value yourself
you've gotta actually do it explicitly.
So if you wanna get the value
and sign to an integer,
you've gotta explicitly dereference the Box.
It's called unBoxing,
that's the term you might see in other languages
like Java for example.
So unboxing, it will explicitly dereference the Box
to give you the contents 42, like that.
So the contents will be then signed
into the integer like that.
So let's have a look at this simple example.
The demos for this chapter are in this folder,
lesson 15, additional Rust techniques.
And for this first demo
we're gonna look at main.rs,
which is obviously entry point,
and then we'll have a at demo_box1.
That's our first example for this section, demo_box1.
Let's take a look.
So in main, I'm gonna uncomment the first called demo_box1
and it's a very simple syntax example of this.
We'll have look at applications of Box,
how you use in the practice a bit later, but for now,
when you say Box new,
it'll allocate this object on the heap.
The Box will have a pointer into the heap,
the Boxed object itself lives on the stack.
And then as I said, with println,
you can dereference the Box explicitly
to give you the contents of what it points to.
Or you can use implicit dereferencing
without the star, println supports both syntaxes.
But if you want to manipulate the value yourself
like I've done here,
then you do need to explicitly dereference.
If you didn't, you'd get a compile error to say,
"This is a Box object,
but you're trying to assign it to an integer,"
that doesn't make sense,
you've got to explicitly dereference the Box
and the Box to the contents of what it points to like that.
And then that value should be 42, which we then print here.
So let's run this first example
just to pay due diligence, we're just gonna see the output.
There's not gonna be any great surprise here.
So using explicit dereferencing
and implicit dereferencing,
explicitly dereference, implicitly dereference,
and then to explicitly dereference
where we need to actually pluck out the value
and copy it into the variable here.
So that's our first example.
Gonna look at another consideration here.
The Box structure doesn't implement the Copy trait.
So when you assign one Box to another, a move occurs.
So it looks like this.
If one Box points to value like that, that's Box1,
and you say Box2 equals Box1,
then Box2 would acquire ownership
and Box1 would lose ownership, a move occurs.
When you assign one Box to another
it moves the value into the new Box
and the old Box is effectively defunct.
So when you assign a Box object to another
a move occurs, you can just about see that.
Box does implement the drop Drop trait,
and that's just as well.
Well, it is a feature of the way Box works
to avoid memory leaks.
When a Box object goes out of scope,
the drop function is called,
and the drop function deallocates the heap-based storage.
So the Box object owns the memory that it points to,
when the Box object goes out of scope,
the drop function will do that before the Box object itself
pops out of scope.
So this has consequences when you pass a Box object
into a function.
We're gonna have a look at that now.
In our project we'll run, have a look at main,
and then we'll have a look at demo_box2.
So let's uncomment demo_box2, and let's take a look.
I've got a simple employee structure,
and employee has a name and a salary.
Fair enough.
I've created an employee object here
with a name and a salary.
That object will be allocated on the heap
and the Box object with container pointed to it.
So this Box object, this boxed_em is a stack-based Box.
In that Box is a pointer
and it points to this employee object on the heap.
So now I pass the Box object into a function.
And this, as you can see by this comment here,
this moves ownership of the Box into that function,
because Box doesn't implement the Copy trait,
doesn't implement copy,
therefore, when you pass it in from here into here,
a move occurs.
So this Box is now defunct.
I can't use it anymore.
If I did try to, if I tried to access the Box object here
I'd get a compile error.
We've seen this so many times now.
If you've lost ownership of a Box, if it's been moved away,
you can't then use it here
after the Box object has been moved.
So that would give us one of those common
trying to borrow a value
after its value has been moved away errors.
So the Box object is moved into here.
So this parameter now, it now owns, pointed to resource,
it is explicitly a Box which points to an employee.
So it is a generic type, it contains a point to an employee,
effectively, inside here, I can use the Box
and print out the employee's name and salary.
At the end of the function,
this Box object goes out of scope,
its drop function will be called,
and the drop function will deallocate the employee
on the heap.
So the object that was allocated here,
the pointer to it is owned by the Box.
And when the Box that owns that pointer goes out of scope,
then the object, the employee object is deallocated.
It is deleted, memory is deleted on the heap.
So I could run this example.
I will run this example
and I'm gonna see what errors might occur
if I try to do things slightly incorrectly.
So let's run it first.
So the employee's created, I'd put into a Box,
the Box was passed by value into the function.
I printed out the employee's details, name and salary,
the Box was dropped at this point,
the employee object is deallocated, the Box object,
I can't use the Box object up here.
If I try to,
then I'm going to get one of those familiar errors
about trying to borrow an object after it's been moved away.
Let's see the error message for the 101th time.
So, yes indeed, so on line 17,
yeah, it is line 17,
on line 17, the value for Box step
was moved into the function, so we don't own it anymore.
So I'm trying to borrow the value
of boxed_em after boxed_em has been moved,
so that's a very common error, you can't use it then.
I got one other thing I wanted to point out.
I dunno if you noticed it, but Box is an object.
It contains a pointer,
and yet here I could just say box.name.
We've seen this before.
When you have references to an object
and then you try to access a member inside that structure,
automatically referencing takes place here
when you access a member.
So if I was gonna do this completely by the book,
I'd have written it this way, I'd have said,
*emp.name
and *emp.salary,
I was being pedantic because emp is a Box,
so the start is the contents operator.
Give me the contents of what you're pointing to,
the employee, and then upon the employee, access the name
and access the salary.
So that is actually what's happening.
But luckily for us, well, it's a happy bit of luck really.
The compiler, when you have a Box,
it automatically will dereference it
when you access a member, when you access a member on a Box
it'll actually dereference implicitly
and access that member on the thing that you're pointing to.
So it'll dereference the Box to give you the employee object
and then access that, the salary inside it.
So I can, just for fun,
I'm going to use the star in one place,
and I'm gonna leave out the star in another place
just to show that both syntaxes actually do work.
Yes, indeed, fantastic, so there we are.
That's an example of the ownership of Box,
Box is allocated on the stack, it doesn't implement copy.
So if we pass a Box into a function, it's moved.
And when the Box goes out of scope,
it's drop function deallocates the thing
that it's pointing to,
it'll deallocate the item on the heap,
which in this case is the employee.