Hopefully, you've had a chance to try the exercise
we introduced at the end of the previous module.
We're going to be talking all about that exercise
in this video.
First, we're going to take a look at the starting
code of the exercise in a bit more detail.
Then, we'll see one possible way
to complete the exercise.
After that, we'll take a look at some solutions
you might have tried that don't work
and result in compiler errors instead.
Getting compiler errors
and fixing them is where the learning happens.
If you got errors while you're working on the exercise,
that's awesome! We'll be addressing some
of the common ones that might be what you've seen.
We'll round out this module by talking about why
this task feels harder than it would
in other languages.
Let's take a close look at the starting code of
the exercise.
There are a few things to notice in this code that are
affected by ownership.
s is a String, which owns its data.
We want to use s when we call the
pluralize function.
We also want to use s in the println
call after the call to pluralize.
There are many possible solutions to this exercise.
Let's take a look at one of them.
We'll start by filling in the pluralize function.
We want the function to take one parameter
of type String,
and we'll call that parameter
singular. The return
type of the function will also be a String.
The body of pluralize will take singular
and add the string, s, to the end.
Because we don't have a semicolon at the end of the
line,
the concatenated string will be the return value
of the function.
Now we can call the pluralize function from
main. The arguments of pluralize
will be a clone of s
and we'll store the result in a new variable,
pl. Now we can
use both s
and pl in the println call.
This solution works;
here's some proof.
OK. Now, let's take a look at some solutions
that don't quite work,
and we'll learn more about ownership in the process.
If we didn't clone s
and passed the clone string to pluralize,
we'd get an error.
There it says use of moved value:
`s` and shows the s was moved
into pluralize.
That means pluralize now owns s,
and s can be cleaned up at the end of pluralize.
So, we can't use s again in the println
in main.
That's why the working solution clones s.
The clone is what is moved into pluralize,
and main continues to own s.
If we clone s in the println
instead of as the argument to pluralize,
we'll get essentially the same error.
Again, we can't use s at all in the
println because it's been moved into pluralize.
While calling clone is part of the correct solution,
it does matter where we call it.
You might not have used an intermediate variable.
You might have called pluralize from the println
directly.
As long as you called clone,
this would also work.
However, if the clone isn't in
the pluralize call, we'll get slightly different
errors.
This time, the error says, cannot
move out of `s`because it is borrowed.
This happens because println borrows its
arguments.
So, it borrows s first,
and then we try to move s into the
pluralize call.
We haven't talked about borrowing yet, but
we will in the next module.
The important thing to know for now is that you
can't move a value while it's borrowed.
This exercise involves some string manipulation
and a function. In many languages,
this would be a very straightforward exercise
to complete.
So, why is it so hard in Rust?
This code seems difficult to write in Rust because
ownership is a different paradigm
than we are used to from other languages.
Rust has performance as a primary
goal, which rules out having a garbage
collector as found in many high-level languages,
as we discussed in the previous module.
However, manual memory management,
as found in C, is too error-prone.
What else is left?
Rust takes a third approach, different from most
other languages,
and helps you manage memory using ownership,
which prevents bugs by cleaning up when
the owner goes out of scope.
Rust makes you conscious of performance
by making slow operations, like cloning,
explicit.
Working within the system of ownership
can feel different than what you're used to.
The benefit of working within the ownership
system is that you get control over
performance while still maintaining
memory safety and avoiding errors.
The good news is that, as you get more experience
working with ownership, it will get easier
and easier to think in the way that Rust works
and code with the grain.
So, will all of our code have calls to
.clone() scattered everywhere?
Probably not. The working solution
we showed for this example actually isn't
idiomatic Rust. What
is idiomatic uses a feature of Rust
called borrowing. The next
module will introduce and explain
how we can use borrowing to make this solution
less annoying, less verbose,
and more efficient.
To sum up this module, we looked
at one way of solving an exercise
involving strings
and functions using cloning to move
data into a function without giving
up ownership.
We looked at some solutions that didn't work
because the data wasn't cloned
or moved in the right way.
We talked about why this feels difficult in
Rust because of Rust's memory management
and ownership features.
Now, we're ready to talk all about borrowing.