- In the previous lesson, we looked at ownership in Rust
and we saw that for types that don't implement Copy,
most types don't implement Copy.
Then when you assign one variable to another
you move ownership to the new variable
and the old variable loses its value
and you can't use it anymore.
So if this is all we had in Rust,
it would be quite restrictive.
Whenever you assign one variable to another,
you lose the original variable.
How would you deal with that in a large program?
Luckily for us, there's another mechanism
which complements ownership,
and it's called borrowing.
Basically it uses references.
You can either think of it as a reference or pointer
whichever you kind of feel easiest to understand.
But basically you can reference an object
without claiming ownership.
It's called borrowing.
Okay? So in Rust, when it talks about borrowing
it means you've got a reference to an object
but you don't own it.
The original variable still owns it.
Okay? So that's what this lesson's all about.
So the syntax for borrowing a value
it uses an ampersand.
I would say at this point, if you are coming
from a background in C or C++, don't try too hard
to compare the syntax because you'll go nuts.
The syntax is different in Rust
and you just have to go with it.
So you put an ampersand before a variable,
in C++ that would mean take the address,
in Rust it means borrow or take a reference.
Let's have a look at this example.
I've got a string object, s, let me draw it here.
S is my string object
and it has a pointer to a buffer on the heap.
Huey one of Donald Duck's nephews, I do believe.
And then I declare a reference variable, r,
borrows the value of s.
Okay? I haven't typed r explicitly here.
I'll show you that syntax shortly.
But when you put an ampersand in front of a variable
it doesn't take the address of, it borrows.
So what we end up with is r, is effectively a reference
to the original variable s, okay?
And effectively an alias for it.
So in your application, when you print out s,
it'll print out huey,
and when you print out r, r is a reference
to that string object, and it prints out huey.
Okay? So reference is kind of automatically
dereferenced on usage to give you the string
that you first thought of.
The thing is that s is still the owner of that string
and r is just kind of like an observer.
Right, so r is a reference variable.
R borrows the value of s, but still s is the owner.
Okay.
Right, if you want to use explicit typing
rather than type inference,
then when you declare a reference variable,
again, you've gotta use an ampersand.
So you use the ampersand twice
once to borrow the value and once to declare
the variable that's gonna hold the reference.
So it looks like this.
And another recommendation, if you're coming
from a C++ background.
In C++, so let me just do a momentary illusion here.
So in C++, if you wanted to declare a reference variable
you would declare it like that with an ampersand
at the end of string.
That would be a reference.
Doesn't work like that in Rust.
In Rust, the ampersand always comes before the value
to borrow it and before the type to make it a reference.
Let's look at this example then.
First of all, I declare s, s is a string variable.
It holds pointer to louie.
Not sure if I spelled that right, but anyway
you know it's duckling number two
in the Donald Duck family,
and r, it takes a reference to s, or it borrows s.
You can say it either way.
A Rust developer would say, I'm borrowing s.
And if you get any error messages
it'll always talk about borrowing.
You can think of it as being, taking a reference to
or you can think of it as being borrowing.
R will borrow or reference if you like,
s, technically r is a reference to a string.
Okay? So the ampersand goes before the type,
r is a reference to a string.
In particular, it references this string, up here.
So again, using explicit typing,
the result's gonna be the same here, isn't it?
When I output s, it'll be louie.
When I output r, it'll be referring to that string
and it'll be louie again.
Okay? So explicit typing is useful.
When you've got functions, it enables you to say
this function takes a reference
to a string or returns a reference to a string.
Borrowing would be the terminology.
You can say a function borrows the value
but it doesn't own it.
R borrows the value of s, but s is still the owner.
Right, you can have mutable borrowing.
If your original object is mutable
then you can have declared via the mutable keyword or mut.
Then you can also borrow the value mutably as well.
You declare a mutable reference like that, okay?
So that means that the reference
is also allowed to change the value.
You can change the value via the reference.
Here's a simple example.
We've completed the trilogy of ducklings here, dewey.
So s, notice the s is a mutable string that's worth noting.
Otherwise we couldn't change anything.
You can't take a mutable reference
unless the actual object itself is originally mutable.
So s is a string object
and it holds a pointer to this text.
Dewey, very good.
And then here you have to take a mutable reference of s.
If I didn't put the mut keyword in here
then I would just have a, my reference would be read only.
Okay? The object might be changeable
but my reference would be not mutable.
So if you want to be able to change the value
to the reference as well
then you have to take a mutable reference.
Okay? So you can change the underlying object.
And if you want to use strong type then,
then you'd have to say r is a mutable reference to a string.
So r is a mutable reference to the string.
So I can change the value via the string.
I can also change the value via the reference
because the reference is also declared as a mutable thing.
So via the reference, I can do a push string.
So, dewey, duck, okay, like that.
So via the reference, r refers to this string object.
So basically it's that string
as we have an alias for, it'll append duck
on the end, when it prints out the r string here
it'll print out dewey duck.
Right, example time for this lesson.
The project is lesson07_borrowing.
If you open that up in the Rust folder, and let's do that.
And the code we're going to run, demo_simple_borrowing.
We'll run it as usual using cargo run.
Let's take a look at that code now.
Okay, so I've opened lesson07_borrowing in VS Code
and same kind of structure as before.
In my main function, I declare various modules
one per section in the lesson
and then I call the appropriate entry point function.
In my demo_simple_borrowing module
called the do_it function.
Okay, so demo_simple_borrowing.
Here's my do_it function.
It shows implicit borrowing
explicitly typed borrowing, and mutable borrowing.
Okay? So these are the three functions down here
that we're going to look at.
We'll run it and then we'll dissect the output
when we look at the code.
Cargo run.
Okay? In demo_simple_borrowing, do_it.
Yes, indeed.
So for the implicitly typed borrowing function
let's have a look at that.
So s1 is a string, huey, r1 is a reference to it.
So I can output s1 or r1.
R1 is effectively just an alias.
So in both cases, s1 and r1
it's the same underlying text obviously.
So that was using implicit typing, which is probably
what you would actually do in practice.
If you want to gain some brownie points, if you just
wanna check your knowledge, you can use explicit typing.
S2 is a string, and r2 is a reference to a string,
in particular, it borrows or refers to s2.
So s2 and r2, r2 is effectively an alias for s2.
So in both those cases it'll print out louie,
in my explicitly typed part, louie and louie.
And then finally, for mutable borrowing,
here's my mutable string, dewey,
take a mutable reference to borrow it mutably,
and put it into a mutable string reference.
Either using s3,
or the thing that refers to it, we can change the value.
So when we output it s, so r3 is dewey duck.
Okay, fantastic.
Right. One other thing, I suppose.
When you declare a mutable reference
if you take a mutable reference like this
then truly the compiler would understand
that r3 is a mutable reference
without you having to actually say it explicitly.
And also s3 is clearly a string, isn't it as well?
So how about if we used inferred typing
to say this is a mutable string
and this is a mutable reference, okay.
Do you think that would work?
Oh, yes.
And that would be the preferred syntax.
You know, there's no particular merit
in typing things explicitly
if you can get by using type inference.
So that would be my preferred approach.