colinfang
colinfang

Reputation: 21727

When does a closure take ownership of its environment without the move keyword?

use std::thread;

fn test2() {
    let x = "abc".to_string();
    thread::spawn(|| {
        foo2(x);
    });
}

fn foo2(x: String) {}

fn test1() {
    let x = 1;
    thread::spawn(|| { 
        foo1(x); 
    });
}

fn foo1(x: i32) {}

fn main() {}

Playground

The error:

error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
  --> <anon>:12:19
   |
12 |     thread::spawn(|| { foo1(x); });
   |                   ^^        - `x` is borrowed here
   |                   |
   |                   may outlive borrowed value `x`
   |
help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword, as shown:
   |     thread::spawn(move || { foo1(x); });

Why does the closure in test1 not take ownership of x, which is specified by the signature (x: i32) of foo1? (I know I can add move to make it work.) I guess it is due to that x is copyable, but if it is copied into the closure then why do I still have the lifetime problem?

However test2 works.

Upvotes: 3

Views: 970

Answers (1)

DK.
DK.

Reputation: 59015

Because it doesn't have to take ownership. Moving is more destructive than simply borrowing, so if the compiler thinks it can get away with not moving a captured value, it won't. It moves the String because it has no other option. It borrows the i32 because it's Copy.

But it can't get away with not borrowing it!

Aah, but the compiler doesn't know that until after it's decided if it's borrowing or moving a captured value. The heuristic it uses is just that: not always correct.

Couldn't it just work it out properly?

Probably, but no one's taught it how.

Upvotes: 4

Related Questions