I don't understand this error related to "move" into loop

Hi,
I'm working my way through the Rust book and have been playing with the chapter 16.2 concurrency examples. I made a small modification to the listing 16.10 and get compilation errors, but I cannot see why.

I put and endless loop around the "for val in vals" loop and it then complains about vals, move and not implementing the Copy trait. Despite Rust's error reporting being generally very good, I'm struggling to figure out this one and understand what the problem is. Here's my code (slightly modified 16.10 example):

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];
        loop {      // <--------- My added line
            for val in vals {
                tx.send(val).unwrap();
                thread::sleep(Duration::from_secs(1));
            }         // <--------- My added line
        }
    });

    for received in rx {
        println!("Got: {}", received);
    }
}

If I either remove my loop{}, or comment out the tx.send line then it compiles.
Can someone explain to me how adding the loop causes a problem?

Thanks,

Simon

In the first iteration of the loop, everything is fine.

However, in the second iteration (and forward), we try to move out of vals because of the for loop - but it was already moved in the previous iteration.

Could you show the code that compiles in this case? I tried to comment it out myself and got the same error, since the problematic code wasn't changed.

1 Like

Sorry, you're right. I think I just observed that the VSCode parser no-longer reported the error on that line.

Simon

Maybe you need to save. rust-analyzer itself does not report those errors, only gets them from cargo check. And it only runs cargo check on save.

Ah, ok. So by iterating through it once means I cannot access it again in the same context? So this produces the same error (enclosing loop also removed):

            for val in vals {
                tx.send(val).unwrap();
                thread::sleep(Duration::from_secs(1));
            }
            println!("{:?}", vals);

So assuming I had a situation where I wanted to access it more than once within the same scope.

If I simplify the code, getting rid of all the example threading stuff to:

fn main() {
    let vals = vec![
        String::from("this"),
        String::from("is"),
        String::from("a"),
        String::from("test"),
    ];
    for val in vals {
        // do something arbitrary
        println!("{}", val);
    }
    println!("{:?}", vals);
}

then the same problem is encountered on the final line. I think I understand now that the loop moves the vector vals into the interator val and so is "gone" afterwards.

How would I borrow the vector for the iterator instead? (I must admit, the intricacies of the borrowing model is my biggest hurdle getting to grips with the language. All the examples and all the courses make sense, until I try to apply it myself.. But I'm sure I'll get there!)

Thanks,

Simon

If you iterate over &vals or &mut vals, you'll get back &String (or &mut String) instead of String, but it will not consume the vector.

Ah yes, thanks. That makes complete sense. I'd actually tried that earlier but I think for some reason it was still throwing the error, like perhaps the rust-analyzer was using cached data.

I found this also works:

    for val in 0..vals.len() {
        // do something arbitrary
        println!("{}", vals[val]);
    }
    println!("{:?}", vals);

Yes, but this is very un-idiomatic. Use iterators whenever you can.

Use an iterator and clone:

for val in vals.iter() {
                tx.send(val.clone()).unwrap();
                thread::sleep(Duration::from_secs(1));
            }