0

I am working on rustling's iterators5.rs and I came to see this solution for this function:

fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
    // map is a hashmap with String keys and Progress values.
    // map = { "variables1": Complete, "from_str": None, ... }
    // map.values().filter(|x| **x == value).count()
    map.values().filter(|x| x == &value).count()
}

As far as I understand it seems like map.values() is returning an iterator of references according to the documentation.

"An iterator visiting all values in arbitrary order. The iterator element type is &'a V." See: https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.values

However, I am not sure why if I do map.values().filter(|x|), Rust Analyzer recognizes x as a type &&Progress?

Is it because in .filter() and hence its closure, only takes in references of the values from the upstream chain? Therefore map.values() produces &x and .filter() only takes on references hence &&x.

I tried finding the documentation but I'm not sure where it says this: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.filter

Edit: Follow up question

Thanks for the comments for pointing out that &Self::Item means that the filter function is taking on a reference from the iterator.

My follow up question would be that since x in map.values().filter(|x|) is of &&Progress type, why does &x make it a &Progress type? Is this some kind of double dereferencing?

And why does x in x == &value of (|&x: &Progress| x == &value) become &Progress?

I would have expected x to be still &&Progress

jcleow
  • 57
  • 1
  • 5
  • It says so right in the definition of the closure: `P: FnMut(&Self::Item) -> bool` means that the closure must take in a reference to the x. – Einliterflasche Apr 05 '23 at 13:52
  • The signature has `where P: FnMut(&Self::Item) -> bool`, and `Self::Item` is `&Progress`. – user3840170 Apr 05 '23 at 13:52
  • Thanks, now I know that `&Self::Item` means it is a reference – jcleow Apr 05 '23 at 14:06
  • 2
    *if `x` is `&&Progress`, why does `&x` make it a `&Progress` type* - because `x` is a pattern, and in patterns `&` dereferences. You can think of it being a mirror image, so that `if let Some(x) = option` "strips" `Some()` off the option, so `&x` in a pattern "strips" the `&`, i.e. dereferences the value. – user4815162342 Apr 05 '23 at 14:20
  • Thanks @user4815162342, is this behaviour covered under deref coercion? I am reading the docs: https://doc.rust-lang.org/reference/type-coercions.html#coercion-types but I am not sure specifically which part is talking about this. – jcleow Apr 05 '23 at 14:45
  • No, that's a [reference pattern](https://doc.rust-lang.org/reference/patterns.html#reference-patterns) and has nothing to do with deref coercion. – cafce25 Apr 05 '23 at 14:53
  • Seems like another answer: https://stackoverflow.com/questions/57832145/dereference-a-closure-parameter also suggests that |&x| is a reference pattern, though I am not entirely sure what it means to be a reference pattern. Does it mean that as long as I use a & infront of a variable and some kind of matching is done its a reference pattern? – jcleow Apr 05 '23 at 15:08
  • 2
    Please scroll up on my previous link and find the words "Patterns are used in:" along with a list where they are used, reference patterns are just a part of all patterns and yes, anything in a patternthat has a `&` is a reference pattern. – cafce25 Apr 05 '23 at 15:40

1 Answers1

2

Compare the signatures of the map and filter methods of Iterator:

fn map<B, F>(self, f: F) -> Map<Self, F>
where
    Self: Sized,
    F: FnMut(Self::Item) -> B;

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

The closure used by map takes ownership of values so that it can manipulate them, deconstruct them or use them as part of some new value. On the other hand, filter's closure doesn't do anything with the items; it just needs to look at them in order to decide if they should be included or not, so it can take them by reference.

Some iterators are over values but some, like HashMap::values, are over references. That means the the associated Item type of the HashMap::<T>::values iterator is &T, not T. The closure supplied to map can accept &T, but the closure supplied to filter needs to accept &&T.

Rust's auto-deref and deref coercion rules mean that this is rarely a problem. You can mostly use a &&T or &&&T etc as if they were a &T.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204