Why is "&&" being used in closure arguments?
a
is of type [i32; 3]
; an array of three i32
s.
[i32; 3]
does not implement an iter
method, but it does dereference into &[i32]
.
&[i32]
implements an iter
method which produces an iterator.
This iterator implements Iterator<Item=&i32>
.
It uses &i32
rather than i32
because the iterator has to work on arrays of any type, and not all types can be safely copied. So rather than restrict itself to copyable types, it iterates over the elements by reference rather than by value.
find
is a method defined for all Iterator
s. It lets you look at each element and return the one that matches the predicate. Problem: if the iterator produces non-copyable values, then passing the value into the predicate would make it impossible to return it from find
. The value cannot be re-generated, since iterators are not (in general) rewindable or restartable. Thus, find
has to pass the element to the predicate by-reference rather than by-value.
So, if you have an iterator that implements Iterator<Item=T>
, then Iterator::find
requires a predicate that takes a &T
and returns a bool
. [i32]::iter
produces an iterator that implements Iterator<Item=&i32>
. Thus, Iterator::find
called on an array iterator requires a predicate that takes a &&i32
. That is, it passes the predicate a pointer to a pointer to the element in question.
So if you were to write a.iter().find(|x| ..)
, the type of x
would be &&i32
. This cannot be directly compared to the literal i32
value 2
. There are several ways of fixing this. One is to explicitly dereference x
: a.iter().find(|x| **x == 2)
. The other is to use pattern matching to destructure the double reference: a.iter().find(|&&x| x == 2)
. These two approaches are, in this case, doing exactly the same thing. [1]
As for why Some(&2)
is used: because a.iter()
is an iterator over &i32
, not an iterator of i32
. If you look at the documentation for Iterator::find
, you'll see that for Iterator<Item=T>
, it returns an Option<T>
. Hence, in this case, it returns an Option<&i32>
, so that's what you need to compare it against.
[1]: The differences only matter when you're talking about non-Copy
types. For example, |&&x| ..
wouldn't work on a &&String
, because you'd have to be able to move the String
out from behind the reference, and that's not allowed. However, |x| **x ..
would work, because that is just reaching inside the reference without moving anything.
1) I thought the book explanation was good, maybe my example with .cloned()
below will be useful. But since .iter()
iterates over references, you have to specify reference additionally because find
expects a reference.
2) .iter()
is iterating over references; therefore, you find a reference.
You could use .cloned()
to see what it would look like if you didn't have to do deal with references:
assert_eq!(a.iter().cloned().find(|&x| x == 2), Some(2));