Why does filter()'s closure take &Self::Item, but any()'s closure takes Self::Item?

A question a student I mentored asked on Exercism:

Why does .any(char::is_uppercase) work, but filter(char::is_uppercase) gives a compile error?

I saw the only difference is that any()'s closure takes a Self::Item while filter()'s closure takes a &Self::Item. I can kind understand why it doesn't compile, but I don't understand why the closure's parameter's type is different. Why does filter()'s closure take a reference and any()'s closure a value?

1 Like

This is because the filter callback cannot assume ownership of the value produced by an iterator. For the values that fit the condition, the iterator produced by calling filter will have to output these values. If the callback took ownership, the value would be gone.

But any is itself a predicate function. The callback can take full ownership, because the iterator itself doesn't need it anymore.

As a non-iterator example:

if condition(&value) {
    return Some(value);
} else {
    return None;
}

The condition function can't take ownership, as the if needs the value to return it if the condition is true. However, if we just want to know the existence:

if condition(value) {
    return Yes;
} else {
    return No;
}

can give up the ownership to the condition function.

9 Likes

Thanks! This is a great explanation.

So am I correct that the filter(char::is_uppercase) doesn't compile because it wants to call a closure expecting a reference to a type on a value of that type?

1 Like

Yes, that's exactly it. The is_uppercase function implements FnMut(char) -> bool, but filter wants a FnMut(&char) -> bool (with resolved types for demonstration purposes).

2 Likes

"As &mut Any, there is also the downcast_mut method, for getting a mutable reference to the inner value. Box adds the downcast method, which attempts to convert to a Box. See the Box documentation for the full details." Per the documentation.

I had a follow up question. You referred to 'Any' as a predicate type. Could you clarify that a bit and provide examples?

Is 'Some(x)' also a predicate type?

@phaylon said "any is […] a predicate function" (emphasis mine). Which is to say that it takes an iterator Item input and returns a bool. That returned bool is independent of the data being iterated over. As opposed to filter, which accepts a predicate function as input, but which returns a Filter struct that contains the iterator that it is filtering.

Also, the any method of the Iterator trait should not be confused with the Any trait, which is for emulating dynamic typing.

No. In this context, I belive @phaylon was using the term "predicate function" to refer to one that is applied to elements of an iterator and returns a bool. See all the parameters to Iterator methods which have the name predicate such as find and skip_while. Anything that takes an iterator's Item as input and returns a boolean can be a predicate function.

Some(x) is an expression which creates an Option enum value which is the Option::Some variant with associated value x. You typically just see Some(x) because use Option::Some; is part of the prelude that is automatically included.

3 Likes

Thanks baumanj that clarifies it and I have since reread the docs on the Any trait. I thought I was seeing something new here but misunderstood what I read. Duh.

I have a minor interest in dynamics and have played with implementing external (PICK databasic) language syntax, overloads and data structures in C#. The point being I am a fan of the Any trait and unsafe language features. Nicely done.

So then... I want to pay close attention to leveraging predicate logic during design then? I saw the potential of large bit mapped results for example. I am not sure.

I am not clear on how much consideration I should give to functional programming in general but am very impressed with Haskel and their community.

It occured to me once again in studying the memory management and cache lines. IE. The dirty and invalid bits and interaction between L1 and L2. Performance oriented from the ground up.

Predicates have use cases for sure. Filter looks good too. The static features seem important as well. I like this language a lot.

Also, I think NTFS has a component using bools for occupied disk doesn't it?

I still struggle with the low level thinking needed for Rust. However, every time I think I will need write a library I find a crate. BTree libraries (2015) for example. Awesome.