0

I just finished reading the Rust book. I've had the following question right from the first few chapters and was hoping that it be would explained later in the book, but that quite didn't happened.

Why does adding an & to the for loop's pattern matcher cause Rust to attempt to move instead of just match? Please see the source code below for an example.

#[derive(Debug)]
struct Structure(i32);

fn main() {
    let arr = [Structure(1), Structure(2)];

    // Manually going through the iterator
    let mut iter = arr.iter();
    let first: &Structure = iter.next().unwrap();
    let first_ref: &&Structure = &first;  // Taking a reference to a reference
    println!("{:?}", first_ref);  // Works as expected

    for elem in arr.iter() {  // Here elem is of type &Structure
        print!("{:?}", elem);
    }
    println!("");

    // The following code does not compile.
    // for &elem in arr.iter() {
    //     print!("{:?}", elem);
    // }
    // println!("");
    // This is the error that I get
    // data moved here
    // move occurs because `elem` has type `Structure`, which does not implement the `Copy` trait
}
Shridharshan
  • 1,307
  • 2
  • 9
  • 9
  • 1
    All assignments in Rust work by _pattern matchig_. The left-hand side is considered as a _pattern_ that the right-hand side is matched against. If the pattern is `&elem`, and you match it against an item of the type `&Structure`, then `elem` will have the type `Structure`. – Sven Marnach Nov 13 '20 at 12:24
  • (Not quite all – only assignments binding new names.) – Sven Marnach Nov 13 '20 at 12:47

1 Answers1

2

The pattern matcher in general works the following way -- on the right-hand side you pass an object that you want to tear apart and on the left-hand side you describe how that object's structure looks like, and inside this description you also mention your own identifiers that you want to contain the pieces of your torn apart object:

let opt = Some("abc");
let Some(s) = opt;

// match `s` to the inner part of the `Option`, so `s` will contain "abc"
struct Point {x: i32, y: i32}
let p = Point{ x: 1, y: 2 };
let Point { x: my_x, y: my_y } = p;

// match `my_x` to `x` part of `p` and match `my_y` to `y` part of `p`, so my_x == 1, my_y == 2 

Your example is no different - this is not "taking by reference" it's a description of how you want to tear the object apart, so:

for &elem in arr.iter() { 
     // `arr.iter().next()` yields `&Structure`
     // your description says - match `elem` to `Structure` part of `&Structure`
}
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Alexey S. Larionov
  • 6,555
  • 1
  • 18
  • 37
  • This makes sense. But I didn't tell rust to move, only wanted to say "match `elem` to `Structure` part of `&Structure`". Updated the question accordingly. – Shridharshan Nov 13 '20 at 12:35
  • 1
    @Shridharshan in Rust everything is move unless you state otherwise, but in a case of pattern matcher you can't ask it to "borrow" something, you can only say which pieces to match. So the proper (and maybe the only) way is to take a reference inside the loop, because there's rarely an occasion when you want double references – Alexey S. Larionov Nov 13 '20 at 12:43