- Rust has a standard structure type named iterator,
which enables you to iterate over the items of a collection.
You can specify an operation to perform on each element
and you specify that operation as a closure.
So in other words, you pass a closure into a method
to say what operation to perform
upon each element in a collection.
So let's see some examples.
Here's a simple example.
I've got a vector of strings.
Well, technically speaking, they're string slices,
and they're ducks.
Donald Duck and his nephews.
And then here, v.iter returns an iterator object.
And the iterator object has a for_each method.
And the for_each method takes closure.
And the closure specifies what to perform on each element.
I'm sure you've seen this kind of code
in other languages as well.
So it knows that it's a vector of strings or string slices.
Therefore, it knows that each element
will be a string, effectively,
and it'll print the string on the console.
That's a nice simple example of using closures,
passing the closure as a parameter into for_each.
Okay, if you have a closure
that doesn't actually make use of the parameter,
then you can name the parameter underscore,
and then you won't get a compiler called in.
So in this example, here's my vector.
I'm going to for_each string in my vector,
I'm going to iterate.
And instead of printing the duck name,
it'll just print xxx, okay?
So although, the for_each function will pass in
a duckling as a parameter, I haven't actually used it.
So I give it an underscore just to represent a parameter.
I have to receive the parameter from for_each,
but I'm not using it.
So the underscore means I don't get a compiler warning
about an unused variable.
Going further, you can use the filter function
and the map function.
Have a look at this example here.
It takes my vector of ducks and it iterates over them.
The iter function returns an iterator.
Upon the iterator, I call the filter function.
Filter takes a closure
that basically returns a true or false.
It's a predicate.
So this closure will be evaluated for each duck.
And for each duck, for each element,
if the element starts with a D, then retain that string.
So donald and dewey will be retained.
And for those which have been retained,
they get transformed.
The map function does a transformation.
It takes in one of the elements, like donald or dewey,
and it returns in uppercase.
So it'd be capital Donald, capital Dewey.
And whatever's left and whatever's been transformed,
the filtered transformed elements
will then be displayed on the console.
So filter the elements you don't want
or keep the elements you do want, transform them,
and then display them.
Bit like using Java 8 streams,
if you've seen that in in Java.
Right, if you want to,
you can collect the results of an iteration
back into some other container, like a vector.
So in this example, I've taken my vector of strings,
get back an iterator.
Filter, only keep the ducks that end with a letter Y.
Remember, the string has an ends_with method.
It'll only keep huey and dewey because they end with a Y.
It'll then transform them to uppercase,
and then it collects those strings into a vector, okay?
So the collect function is generic.
When you call a collect function,
you've gotta tell it what type of collection
it's going to be.
Is it going to be a vector or some other kind of collection?
So we use that syntax.
We'll have a look at generics in more detail
later on in the course.
But basically, the idea is when you call a generic method
where you've gotta tell it the type name,
you use this syntax,
the name of the method, ::, and then the type parameter.
A vector of string is the collection type of get back.
Right, so that was actually quite straightforward.
Let's have a look at these examples and we'll run them.
Okay, so in main, I'll comment the last demo,
demo_closures_iteration.
One of the main reasons for using closures
is when you iterate in over a collection.
And then here's the code.
So the functions that we looked at,
I've just put into my demo,
and I call those four functions from the top level.
So let's run it and then look at the output,
and just cast our minds over the code as we're doing so.
Cargo run.
Right then, so from the top, here are my ducks.
All ducks, get the iterator object,
invoke the for_each method on the iterator.
For_each takes a closure.
It'll call that closure four times
with donald, huey, louie, and dewey.
And it prints them.
There we go.
And then remember the example
with an unused closure parameter.
When I call for_each,
it will pass a parameter into the closure,
but we don't use it, we just print the xxx.
So these are my redacted decks.
They've been edited out.
And then filter in and mapping,
give me back uppercase ducks that start with a letter D.
So give me back an iterator.
Filter, keep the elements that start with a D,
donald and dewey, then transform them to uppercase.
And then for each one, display.
Okay, so by uppercase D ducks are Donald and Dewey
as advertised.
And then finally, collect into some kind of variable,
specifically into a vector of string,
perform the iteration, get back an iterator.
Filter, keep only the elements that end with a letter Y.
So huey and dewey.
Convert those type of case
and then collect them back into a collection.
I can then do another operation on later.
So collect them into a vector of string.
So this is gonna be a vector of string.
A vector of string which I can then output.
I can say, what's the length of my vector of string?
There were two ducks that ended with a letter Y.
And I can then iterate through my vector.
That's my vector.
Get an iterator object,
and then call for_each to iterate like so.
Okay, so that kind of is quite a nice way
to round off the lesson really.
Closures are really important in Rust
for iterating over collections, for spawning threads,
and for ad hoc usages as well.
So we've looked at the syntax for using closures.
We're going to revisit the topic again
later on in the course when we look at generics and traits.
But we'll actually look at how closures
actually work internally in terms of objects and interfaces.
Okay, but that's a good solid start.