- What we're going to look at now is a closure
that a function, sorry, that receives an FnMut.
So remember all of this basically centers
on the inheritance hierarchy between these traits.
FnOnce is the super trait.
FnMut inherits from it and Fn inherits from that.
So let's imagine we've got a function
that receives that type of parameter.
Okay? So that means you can pass in,
you must pass in something of that type
or something which inherits from that type, like this.
So let's see a function that receives
an FnMut as a parameter.
Here's a function, I've called it receive FnMut.
It takes a function, a mutable function, okay,
'cause it's gonna be mutating state.
Then you have to declare it as mutable.
So a mutable lambda, effectively a closure.
This F is of type FnMut.
So it's a type constraint and it has a signature specifier.
So it means you can basically pass in a closure
which implements FnMut.
So it's allowed to mutate at state,
and the closure you pass in
must serve no parameters and no return.
And it calls the lambda twice.
Okay? So whatever lambda or closure you pass in here,
it'll call it many times and that's fine.
So what can we pass in to this function?
Anything which implements FnMut
or anything which implements Fn.
So let's see an example of that.
In our demo project we run main,
and we're gonna look at demo, closures, FnMut,
and then we'll run the code.
Right, so in our main code, I scroll down to the bottom.
Let's comment out the bit we've done,
and uncomment the next step.
Demo closures of FnMut.
Let's have a look.
It's basically similar to what we had before,
but not quite as flexible.
So here's my function.
I've passed in, it receives,
it's a generic function again.
It receives a generic parameter 'F', where F must be -
when you see FnOnce or FnMut or Fn, it means closure.
Okay? So this function you pass, that you pass in.
This closure must be something which implements FnMut
or a sub trait like Fn.
Basically a closure that takes no parameters
and doesn't return anything.
We call the closure twice.
Whenever closure we pass in, will be invoked twice.
Okay? Now then.
Because this type, this part by here
is constrained to be FnMut,
in the hierarchy, remember,
we had FnOnce, then FnMut, then Fn.
So you've gotta pass in something
that's at least an FnMut.
You can't pass in an FnOnce
because that's too high in the hierarchy.
It's like having a class A, class B, class C.
If the function takes a B,
you've gotta pass in at least a B
or maybe something below it in the hierarchy.
So I can't pass in FnOnce.
FnOnce is above FnMut in the inheritance hierarchy,
it's like a super class or super two.
So I can't pass in FnOnce, okay,
because well the closure calls this function twice.
So if I could do this
then it would call this closure twice
and that would be catastrophic,
because the, it drops the external strength, S1.
This closure can only be invoked once,
it's not safe to pass it into something
that potentially might call it twice.
So we can't pass in an FnOnce,
but I can pass in a closure which implements FnMut.
That's the same closure we looked at previously.
This is my closure, which captures a mutable reference.
It changes S2.
So this closure will implement FnMut
but I can pass that closure into here.
That's exactly what this function was expecting.
A closure that implements FnMut
or something which inherits from that,
such as a closure which implements just Fn.
So you can see why in the previous section
we discussed the concepts of FnOnce, FnMut, and Fn
is critical really to understand this.
Here's a closure.
The compiler will figure out, it doesn't move state,
it doesn't mutate state, it just captures S3
as an immutable reference.
So this closure will implement just the Fn trait.
Fn inherits from FnMut.
So it's okay to pass this into an FnMut parameter.
Okay? So all of this will also work
Cargo run.
Okay, so in my demo of closures for FnMut
I called the receive mut, FnMut function.
I gave it a closure.
My original string was B, B, B
but the closure mutates it and that closure remember,
whatever closure I pass in here
gets passed in as the parameter.
That's the function that gets called twice.
So it'll invoke my closure twice.
So it invokes my closure the first time to append B, B, B.
And then it calls it the second time
to append B, B, B a second time.
So you can see it has mutated it.
And then finally when I pass in my immutable closure here,
my Fn closure again that closure
will get passed in to the parameter
as a function and it calls that function twice as well
which prints out C, C, C twice.
Okay, so the third example we're going to look at
is closures which implement the,
or functions that take an Fn trait.
So let's see that.
If a function calls a closure many times,
and if you want to allow the closure
to capture state immutably,
you're not gonna change it, you're just going to read it,
then your function should basically take a parameter
of type Fn like this.
I've gotta function called receive Fn.
It receives a parameter, a function,
well a parameter of type F where F is a closure,
no parameters, no return value,
basically a closure that doesn't mutate the state.
So again, the important thing
is the inheritance hierarchy of traits
for functions of closures.
FnOnce at the top.
FnMut inherits from it and Fn inherits from that.
So if you have a function that's basically taken an Fn,
you can only pass in that.
Okay? You can't pass in one of those or one of those.
It has to be basically a function
like that that gets passed in.
So let's see how we can call this function
and pass in a closure, which implements just the Fn trait.
You can pass in a closure that implements the Fn trait.
Let's see how to do it.
In our code demo closes Fn,
and this would be our last demo for this lesson.
It's been quite a journey, hasn't it?
So let's run the program when we finish looking at the code.
So in our main code, this is the demo.
Demo closures, Fn do it,
which is here.
So I've got a function which receives some kind of,
it's a generic, it receives some kind of parameter
of type F where F is a closure,
which implements the Fn trait.
So it captures state immutably
or it doesn't capture state at all.
That would also work.
And then it calls the function several times.
Okay? So if you have a function that receives an Fn,
remember the hierarchy, we have FnOnce at the top,
below it we have FnMut.
And then below that we have Fn.
Fn is at the bottom of the hierarchy.
So you must just be able to pass in an Fn parameter.
You can't pass in an FnOnce, okay?
Because that is above Fn in the hierarchy.
You've gotta pass in an Fn, not an FnOnce.
You can't pass in an FnMut either.
So this closure here,
same closure as before.
You can't pass that in either.
You can only pass in something that doesn't move the data.
So it's not an FnOnce and it doesn't retake the data.
So it's not an FnMut.
It must be something that just captures the state immutably.
Okay? So here's a closure
which captures the state immutably.
This closure will be converted into a structure
which implements Fn, the Fn trait.
And that's exactly what this function
was designed to receive.
Receive Fn is meant to receive a closure,
which just implements the Fn trait.
So that'll get passed into here
and then it'll close the function.
Call a function a couple of times.
Okay,
so there we go.
So there are lots of functions in the standard library
that receive closures.
Whenever you see a function, a generic function,
where the generic parameter is constrained
to be FnOnce or FnMut or Fn,
it means you can pass in a closure.
and depending on whether the type parameter
specifies Fn or FnMut or FnOnce,
depending on the type constraint,
you can either pass in a closure
that moves state into the closure,
or a closure that captures mutably the state,
or a closure that captures immutably the state.
So it's quite tricky.
Nobody ever said this was gonna be easy.
It was something that took me a while to figure out
because I've been working in Java
and C sharp and C plus plus for many years
and you kind of know what to expect.
And the closure model, like Lambdas in Rust
is really quite complex.
But have a play with these examples.
Try commenting bits of code out
and add commenting and see what errors you get
and that'll help you understand
the mechanism that Rust is providing for you.
And it's really quite clever.