I\'m coming from a C (and to a lesser extent, C++) background. I wrote the following code snippet:
fn main() {
let my_array = [1, 2, 3];
let print_me
First of all, here's a link to the definition of for in the reference.
To summarise, B
is any expression which evaluates to something that can be converted into a value that implements the Iterator<T>
trait, whilst A
is a irrefutable pattern that binds values of type T
.
In your specific case, slice::iter returns an Iter<i32>, which implements Iterator<Item = &i32>. That is, it doesn't yield i32
s, it yields &i32
s.
Thus, in both the first and second examples, k
is actually binding to &i32
s, not i32
s. When you specified the type of the closure, you were actually specifying the wrong type. The reason the final example works is because A
is a pattern, not a variable name. What &k
is actually doing is "de-structuring" the &i32
, binding the i32
part to a variable named k
.
The "irrefutable" part simply means that the pattern must always work. For example, you can't do for Some(x) in thingy
where thingy
implements Iterator<Option<_>>
; Some(x)
would not necessarily be valid for every element in the iterator; thus, it's a refutable pattern.
Many iterators actually return a reference rather than a value. To be sure, you have to check the return type of .iter()
, which should be of the form Iterator<Item = X>
: X
will be the type of the variable returned.
So here:
fn main() {
let my_array = [1, 2, 3];
let print_me = |j: i32| println!("= {}", j);
for k in my_array.iter() {
print_me(k);
}
}
This X
is &i32
(a reference to i32
), and therefore k
has type &i32
.
This is why, when calling print_me
, there is an error: &i32
is passed where i32
is expected.
There are multiple possible fixes here:
specify a different type to print_me
:
let print_me = |j: &i32| println!("= {}", j);
dereference the value of k
:
print_me(*k);
change the type of k
by destructuring in the loop:
for &k in my_array.iter() { ... }
The destructuring occurs because for .. in
accepts an irrefutable pattern, so you can pattern match like you would do in a match
expression, except that the variable's type has to match (otherwise you get a compiler time error).
To better illustrate it, we can use a slightly more complicated example:
fn main() {
let my_array = [(1, 2), (2, 3), (3, 4)];
let print_me = |a: i32, b: i32| println!("= {} {}", a, b);
for &(j, k) in my_array.iter() {
print_me(j, k)
}
}
The type of my_array
is [(i32, i32)]
: an array of tuples of 2 i32
. The result of .iter()
is therefore of type Iterator<Item = &(i32, i32)>
: an iterator to a reference to a tuple of 2 i32
aka &(i32, i32)
.
When we use the irrefutable pattern &(j, k)
what happens is that we destructure the tuple so that:
j
(inferred to be of type i32
, only works because i32
is Copy
)k
((inferred to be of type i32
)j
and k
thus become temporary copies of the i32
inside this element.