How to make one tiny change to Iterator.filter()? (Wrapping the predicate)

I'm lost in the weeds of generic programming. Here's the implementation of filter() in the rust source:

    fn filter<P>(self, predicate: P) -> Filter<Self, P>
    where
        Self: Sized,
        P: FnMut(&Self::Item) -> bool,
    {
        Filter::new(self, predicate)
    }

Makes perfect sense. I'm trying to implement reject() by wrapping the predicate in a function which negates its output. Sounds easy, but I can't figure out how to create a function or closure which implements FnMut. I'm getting "expected type parameter P ... found closure ...".

Here's my first naive attempt, which returns that error:

    fn reject<P>(self, predicate: P) -> Filter<Self, P>
    where
        Self: Sized,
        P: FnMut(&Self::Item) -> bool,
    {
        Filter::new(self, |item| ! predicate(item))
    }

Apparently, a closure will implement FnMut in certain cases, but I haven't been able to.

The problem here is that wrapping a function in closure changes its type. You're saying that you will create a Filter<Self, P>, but you're really creating a different Filter<Self, F>, where F is the (unnameable) type of created closure.

Unhappily, there's little we can do here, due to this unnameability. You can't explicitly name the output type of reject, since you don't know it. You can't use impl Trait syntax either, since this is not a free function. And even if you could, Filter::new is private, so you can't call it in your code.

How about creating a closure with a mutable reference to predicate so that it'd implement FnMut.?

Or, what about a function factory which receives predicate as a parameter and produces a function with type P?

The only nameable function type you can create on stable by wrapping a generic Fn* is Box<dyn Fn*>, no matter what you do. On nightly, you can try and implement FnOnce and FnMut manually.

Here's an example of the FnOnce + FnMut version:

1 Like

That's fantastic - thank you.

A more stable approach would be to implement your own copy of the Filter struct which does the same thing applies the function in the opposite polarity.

It's definitely not as nice as just reusing Filter, but I believe it's the best you can do on stable.

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.