What is the exact definition of the for loop in Rust?

后端 未结 2 1547
轻奢々
轻奢々 2020-12-20 19:33

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         


        
相关标签:
2条回答
  • 2020-12-20 19:37

    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 i32s, it yields &i32s.

    Thus, in both the first and second examples, k is actually binding to &i32s, not i32s. 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.

    0 讨论(0)
  • 2020-12-20 19:56

    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:

    1. specify a different type to print_me:

      let print_me = |j: &i32| println!("= {}", j);
      
    2. dereference the value of k:

      print_me(*k);
      
    3. 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:

    • the first element binds to j (inferred to be of type i32, only works because i32 is Copy)
    • the second element binds to k ((inferred to be of type i32)

    j and k thus become temporary copies of the i32 inside this element.

    0 讨论(0)
提交回复
热议问题