- In this section, we're going to see how to use
type inference to simplify closure syntax.
So first of all, a quick recap
of explicit typing with closures.
Have a look at this simple closure here.
Note the following points.
We specified type info explicitly
for the parameters and for the return type.
And also the body of the closure
is enclosed inside curly brackets.
So this is full syntax for closures,
but we can simplify the syntax quite a bit actually.
So you can omit the parameter types.
Rust will infer the parameter types
based on the values you pass in
the first time that you call the closure.
You can also omit the return type.
Rust will infer the return type based on
how the algorithm goes inside the closure to see
what type of value you've actually returned in practice.
So here is a simpler closure, get_timestamp.
It's a closure, it doesn't take any parameters,
but we haven't specified the return type
and we haven't specified curly brackets either.
Okay, so we have simple syntax.
The return type isn't specified explicitly.
It can figure it out for itself
based on what it sees we've returned.
And also we've omitted the curly brackets
around the body of the closure.
So it all looks a bit neater.
So in this case, Rust will infer,
it'll guess, it'll see clearly
that the closure returns a DateTime of UTC.
That's what this function returns.
When you call the now function on UTC,
it actually returns a DateTime used in the UTC time zone.
So you can omit parameter types and return type
and you can omit the curly brackets
if it's a simple expression.
And that's what people would tend to do.
So here's another example.
This closure takes a parameter, n.
We haven't specified the type of n,
so Rust will infer the type of the parameter
the first time we call the closure.
So it'll note the closure here.
At the point when we define the closure,
it doesn't yet know what type n is going to be,
but when we call the closure, it basically binds n
to be the parameter that we've passed in.
So at this point, at the point of the call,
the compiler will realize
that the parameter is a 64-bit float.
And then while the return value will also be a 64-bit float
because you can see the expressions that we've got.
So once you've called a function
and you've bound the parameter to a particular type,
you've gotta be consistent, okay?
So it is not like it'll regenerate
the closure for each call.
Once you've invoked the closure,
at that point, the compiler has decided
that this is going to be a 64-bit float.
So if you try to call it again a second time
with a different type, you'd get an error
because the closure now takes a 64-bit float
and we've passed in an integer instead.
It's the wrong type for what the compiler thinks it needs.
So the first call is the important one.
That's where the compiler makes the decision
about what type it's going to be.
And you have to then be consistent thereafter.
Right, well, let's see an example in the demo project,
lesson09_nested_functions_closures.
We'll have a look at demo_closures_inferred_types
and then we'll run it.
Let's have a look at the code.
Right, so in main, let me uncomment
the demo_closures_inferred_types
and let's have a look at it.
So in here, I've got a few functions
that illustrate closures with no parameters,
with one parameter, with many parameters,
and with multiple statements.
And here's the code.
So a closure with no parameters and explicitly,
implicitly infer in the return type.
So when we call it, it knows that it returns
a DateTime UTC, which we can then format as the time.
And here's the second closure.
At the point here, the compiler doesn't know yet
what type n is going to be, but then when it sees
that we've called it with a float 64,
at that point, the compiler will realize it's a float 64.
Therefore, if we try to call it a second time
with some other type, we'd get an error on this line.
So I'm just gonna prove the point.
I'm gonna do a cargo check of the syntax
and I think I'm gonna get an error here on line 28.
Let's see.
Right, okay.
So it says we have mismatched types.
Right, on line 28, the call the reciprocal, it was expecting
a floating point number and it found an integer.
In other words, the arguments to this function call
are incorrect because the closure,
it has already inferred the type to be a float 64
and that's not a float 64.
So the point when you define the closure,
the type isn't known yet for the parameter,
but when you call the function, at that point,
it binds the type to be float 64, okay?
So n must be float 64.
I could have passed in 5.0, but I can't pass in five.
Remember, Rust doesn't do automatic type conversions.
So that statement wouldn't work.
And then what have we got here?
So I've got a closure that takes in
a couple of parameters and it returns the product.
Look how tidy that is, much easier.
We don't have to say a is an i32 and b is an i32.
Oh, and it returns an i32,
and here's the body of the lambda.
That's the kind of syntax we had before.
But when we call the closure,
the compiler can see that those are 32-bit integers.
We don't need to tell it.
It can see that clearly.
So it knows that a and b are 32-bit integers.
And if we return a multiplied by b,
then clearly that is also an i32
and we don't need the curly brackets either.
Okay, so in practice, the syntax
for closures is really quite elegant, I think.
And then finally, this example,
a multi-statement lambda or closure, I should say.
When we call a function, we pass in the value five, okay?
So that's the number of seconds.
That's a 32-bit integer.
You can still have curly brackets
if you want to perform multiple statements.
And then finally have an expression at the end
and that's the return.
And remember, if you have an expression
at the end of a lambda or closure or function,
it's as if it was an explicit return statement.
So you could say return statement, but in practice,
you'd probably just say expression like so.
Okay, so all that remains is for me
to actually run it to confirm that it does indeed work.
Hope so.
So I've got a five second delay in one of my demos
so that's why it's pausing a little bit.
So there we go, all the functions have worked nicely.
So in practice, when you are using closures,
you will use this abbreviated syntax
to give you a more elegant approach.