3

The signature of Iterator::filter is

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

Because it has self as the first parameter, I was assuming I would have to pass the iterator by value, thus moving ownership to that function. However, I am able to call it with just a ref mut and the code compiles and runs. For example:

fn char_count(i: &mut impl Iterator<Item=char>, c: char) -> usize {
    i.filter(|&x| c == x).count()
}

What am I missing here?

Herohtar
  • 5,347
  • 4
  • 31
  • 41
josecm
  • 413
  • 3
  • 15
  • 3
    at some point, every rustacean understand that `self` can be a reference, and then a new world open to them, full of flower and happiness. – Stargateur Jul 26 '21 at 22:02
  • I think I see where you are getting. Care to elaborate or direct me to some reference? – josecm Jul 26 '21 at 22:07
  • 3
    `Iterator` is implemented for every [`&'_ mut Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#impl-Iterator-13) so if the original Iterator is Sized, filter is implemented, and so filter is implement on `&mut Iterator` so `self` is `&mut Iterator`. Magic ! – Stargateur Jul 26 '21 at 22:11
  • Thanks for the comments. I now get the main idea. – josecm Jul 26 '21 at 22:15
  • 1
    @Stargateur Write that in an answer! – Kevin Reid Jul 27 '21 at 00:15

1 Answers1

-1

There is confusion about the two parameters here.

The self parameter refers to the original iterator. That is, filter() moves whatever the original iterator is into the new Filter struct, which takes ownership of that iterator (the Self in Filter<Self, _>)

The predicate is a FnMut because it does not need to be an Fn. That is, the function provided as predicate is allowed to modify the context it captures while it executes. This is possible because Filter itself does not borrow predicate when it calls it. If it did, predicate would have to be an Fn(&Self::Item).

The predicate takes the iterator's items by reference. Notice the sigil in the signature FnMut(&Self::Item).

user2722968
  • 13,636
  • 2
  • 46
  • 67
  • 1
    "filter() moves whatever the original iterator is into the new Filter struct, which takes ownership of that iterator" My question hits this point exactly. How can you move the "original iterator" to the new filter struct if in the char_count function you only have a reference to it? – josecm Jul 26 '21 at 21:57
  • 3
    Move the reference. A `&mut T` is just another type `U`. And there is an [impl in the stdlib](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#impl-Iterator-13) for any `&mut T` where `T` is `Iterator` to also implement `Iterator`. – user2722968 Jul 26 '21 at 22:01
  • Now I see! Thank you for the clarification – josecm Jul 26 '21 at 22:08