- In the first three sections in this lesson,
we've been showing how to pass parameters into a function.
And so the last three sections in the chapter,
we are going to see how to return something from a function.
So we'll kick off by looking at how to return a value.
Then we'll see how to return a reference,
and then finally we'll see how
to return a mutable reference.
Okay? So first of all, the general syntax.
When you want to return something from a function,
this is how you specify the return type.
This function here takes some parameters
and then you say arrow
and then the type that the function returns.
Okay? So that's the return type from the function.
If you don't specify a return type,
so in other words, if you didn't have anything here,
then implicitly the function has no return value
and that would be like void in other languages.
So as it stands now,
the function doesn't return a value.
Right, the next thing to consider
is how do you actually return a value of that type?
Well, you use the return keyword, no surprise there.
So this function indicates that it returns some type.
So then you must have in your function
a return statement which returns some value of that type.
And remember, Rust is quite fussy about type correspondence.
You've gotta return the value of that actual type.
There is a shorthand syntax.
You can do this, if the last bit of your function
is an expression, okay, so not a statement.
In other words, no semicolon, if it's just an expression,
so there's no semicolon here, there isn't, okay?
Then this expression is implicitly taken
to be the return value.
So this is what most Rust developers do.
They just have a value at the end of the function
and that's implicitly the return value of that type.
Okay, so first of all, then how do you return a value?
It depends on whether it's a copyable type,
like int32, or a non copyable type like string.
So first of all let's discuss returning a copyable value,
a type which implements the copy trait like i32.
So in that case the value that you return
will be bit copied to the calling function.
It's quite straightforward. So here's an example.
Starting at the top, I have some higher level function.
I come in here, I call f1
So we call f1,
and f1 indicates that it's going to return an i32.
So in the function we do indeed return an i32.
So n here is a local variable. It has the value 42.
When we return n from the function,
that value n is bit copied up to here.
Okay? So that value then disappears,
and the variable in our function up here is a copy
of the value that was returned.
Okay? So basically the value here is bit copied
into the return like so,
because i32 implements the copy trait.
So that's nice and easy.
And now we are gonna consider what happens
when you return a non copyable value,
something like a string,
something which doesn't implement the copy trait.
So in that case, when you return, for example,
a string from a function,
Rust moves ownership from the function back to the caller.
The caller then becomes the owner of that string.
Right. So here's an example again,
let's take this example from the top.
Here is my sum higher level function, and I call f2.
f2 indicates that it's going to return a string object,
which is nice.
So inside the function, I create the string object, s,
let's draw it here, and it contains the text, "hello"
like so, and when I return s,
basically it'll take a bit copy
of this then and pass it into here.
So this string variable here, which I've also called s,
that also has a pointer to the underlying text.
And the original string object here
loses ownership of that text.
So effectively this then has relinquished control
and transferred control back up to this string here.
Okay? So the string object that was owned here,
effectively ownership gets transferred
up to the calling function.
And this function now owns the string,
which I guess means that
at the end of some higher level function
when this outer string goes outta scope,
at that point the string will be dropped
and the text will be deleted.
Okay? So when you return the value,
it'll either do a bit copy if it's a copy type
or it'll move ownership
into the recipient return value basically,
and the return value then owns that data.
Right, one small caveat, this doesn't work.
Imagine you tried to return a string literal.
The purpose of this function,
I've called it bad funk because it won't compile,
it was hoping to return a string object, okay?
But, and what it tries to do, it takes the mark,
like the exam mark, and it hopes to be return
either this string literal or this string literal,
but it doesn't work because a string literal
is a reference, a string reference.
There's no automatic conversion from a string reference
to a string object.
It was hoping to return a whole string, not a string slice.
So that syntax wouldn't work.
If you really did want to be return a string,
you'd have to say string colon colon from pass, okay?
In which case this string object would then be copied back
to the caller and the caller would then adopt ownership
of that string.
So if you do want to return references like a string slice,
we'll talk about that later on.
In the next demo, we'll see how to return a reference
if that's what you really want to do.
But for now, let's just have a look
at the demo for returning the value.
So here we are, this is my lesson eight functions demo.
In the main code, let's uncomment the relevant demo,
demo return in value, that one there,
and it's here.
So let's have a look at the code.
In my do it function
I call the function that returns a copyable type.
So that's this function here.
It declares a local integer
and then when it returns the integer,
the bits of this integer are bit copied
into the recipient function.
So that's effectively a copy.
That's the first function.
And then the second function call
function returning a non copyable type,
well that's this one here.
And it returns a string object by movement.
Here's my local string.
When I return that string, this string,
the ownership of that string will be moved
into the return value here.
Okay? So this string, this variable here,
adopts ownership of the text.
And this is now the owner
which means that when s goes out of scope,
that's when the string will be actually de-allocated.
And then finally, as I was saying,
if you tried to return a string literal from a function
when the function was actually expecting a whole string,
then this wouldn't compile.
I'll just uncomment this code just to show you the error
and then we'll comment the code back up again
and run it properly.
So let's just open a quick terminal window.
I'll just do a quick cargo check.
I'm expecting a compiler error on line 27.
Well look at that.
So it says, "On line 27, you are trying to return a,"
well, according to the function, it was expecting
that you're going to return a string object.
But instead, you've either returned a string literal,
sorry a string literal here, like a string slice,
or it's gonna return this string slice.
In either case, it's not a whole string object.
It considers whether you should call
the two string function.
If you have a string literal and you call two string,
it actually converts into an actual string object
and that would actually work here.
But anyway, let's just comment that out
(typing)
and let's just run the code that we hope will actually work.
So I'll save the file and I'll run it.
(typing)
So I'm hopefully expecting this to work now.
Okay, yes, it's worked okay.
So initially I called the function
that returned a copy of n, the number 42.
And then in the second case, I called the function
which had a local string
and moved ownership of that string into here.
Okay? And that's the string, "Hello".
Incidentally, if you wanted to simplify this code,
if you really did want to write something that simple
then I could have just said 42.
Okay? And without the semicolon.
That's an expression, that's implicitly the return value.
And likewise here, if this really was all I wanted to do,
I could have just written it like this,
again without the semicolon,
so it has to be an expression, not a statement.
And then I could have got rid of that statement there.
So if you have a dangling expression
it is implicitly taken as the return value.
So that would also work.
Let's just prove the point, run it again,
and it works.
Magic.