Rust(version = 1.53.0) doesn't need to explictly use `move` keyword to transfer ownership in closure?

I know from using-move-closures-with-threads that when closure take ownership of captured variable should use move keyword.

In Rust (version = 1.53.0), I remove move keyword before the clousre, and it is successfully compiled.

fn main() {
    let v = vec!["hello", "good", "nice"];

    let _ = std::thread::spawn(|| {
        for value in v {
            println!("v = {}", value);
        }
    }).join();
}

From just after the example in that chapter:

Rust infers how to capture v , and because println! only needs a reference to v , the closure tries to borrow v . However, there’s a problem: Rust can’t tell how long the spawned thread will run, so it doesn’t know if the reference to v will always be valid.

In your example, for value in v doesn't only need a reference -- it's needs ownership of v. Thus Rust infers that it needs to capture v (and not just a reference to v) from the start, and you don't need the move.

(Note that they used println! on v itself, unlike your code inside the loop.)

If you alter your code like so:

-        for value in v {
+        for value in &v {
             println!("v = {}", value);
         }

Then the for loop only needs a reference, and you get the error which suggests adding move. (And adding move does resolve the error.)

2 Likes

It should use move keyword, since for v in value take the owership. It should be compiled error but my code is successfully compiled in Rust 1.53.0.

The fact that for v in value takes ownership is exactly why move is not needed. The closure doesn't make sense unless v is captured (moved), and so it is -- even without the move keyword.

Another example:

struct MyNonCopyStruct;
impl MyNonCopyStruct {
    fn takes_ownership(self) {}
    fn only_needs_borrow(&self) {}
}

fn main() {
    let v = MyNonCopyStruct;
    let w = MyNonCopyStruct;

    // OK without `move` because taking ownership implies `v` _must_ be captured (moved)
    let _ = std::thread::spawn(|| v.takes_ownership());

    // Error because only a reference to `w` is captured and it may not live long enough
    // let _ = std::thread::spawn(|| w.only_needs_borrow());

    // Fixed if we _explicitly_ move even though only a reference is used
    let _ = std::thread::spawn(move || w.only_needs_borrow());
}
2 Likes

It makes sense~ thxs

BTW, string literals are &'static and thread-spawning closures are allowed to use 'static references.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.