So the thing here is that FnMut
is a trait, and you can't have values of type FnMut
directly. You can have a MyClosure
type that implements the FnMut
trait, and functions can say that they accept any type implementing a trait.
Let's ignore the return type for now. Here's are two equivalent ways to write filter
(without the return type).
// with impl Trait
fn filter<A>(pred: impl Fn(A) -> bool) {
...
}
// with generics
fn filter<A, F>(pred: F)
where
F: Fn(A) -> bool,
{
...
}
The first way of writing this is just a shorthand for the latter method using generics. Since you can't use the trait Fn
as its own type, we use generics to duplicate the filter
method for every choice of F
, and each duplicate will accept some specific concrete type we name F
, and we know that F
implements the trait described in the bounds.
The question is now: "What happens with the return type?". Because if you try to write it with generics
// doesn't work
fn filter<A, RetF>(pred: impl Fn(A) -> bool) -> RetF
where
RetF: impl FnMut(Arg) -> Ret,
{
...
}
Then we are saying that we duplicate filter
for every possible RetF
that implements the FnMut
trait with those arguments, but that's not really what we want.. We want the specific type of the closure we construct inside the body. For this purpose, Rust provides a feature known as existential types that allow us to do exactly that. It is written as impl Trait
like we saw in the argument, but it means something different than when we used it as an argument. So what we really want is this:
fn filter<A>(pred: impl Fn(A) -> bool) -> impl FnMut(Arg) -> Ret {
...
}
This tells the compiler that the return type is some specific type that implements the trait, but we give it no more information, and it will figure it out from the body.
But what about the argument to our returned closure? Well we want to be able to give it a closure, but we might want to call it with multiple different closures. So by the arguments above .. we want it to be a generic too? But the generic shouldn't be on filter
, because then after it is returned, the argument would still be one specific type, and not be callable with any closure.
To solve this, Rust provides a special type denoted as dyn Trait
. When we use the dyn
keyword like that, we are referring to a special type known as a trait object, and all values that implement the trait can be turned into the trait object type. That said, trait objects can't be "bare", they have to be behind a pointer of some sort, most commonly a Box
.
That makes our argument type written as Box<dyn FnMut (Vec<A>, A) -> Vec<A>>
. We also have to use a trait object for the return type — existential types are not allowed in that position.
Finally you will end up with this signature:
fn filter<A>(pred: impl Fn(A) -> bool) ->
impl FnMut (
Box<dyn FnMut (Vec<A>, A) -> Vec<A>>
) -> Box<dyn FnMut (Vec<A>, A) -> Vec<A>> {
...
}
Now you will run into some issues with cloning your A
and sharing your predicate among returned closures, but you can fix those with some + 'static
and perhaps sharing the predicate with an Rc
.