skelix
skelix

Reputation: 51

Why is the move keyword needed when returning a closure which captures a Copy type?

godbolt

fn foo(c: char) -> impl Fn() -> i32 {
    || bar(c)
}

fn bar(_: char) -> i32 {
    42
}

Which throws an error

error[E0597]: `c` does not live long enough
 --> src/lib.rs:2:12
  |
2 |     || bar(c)
  |     --     ^ borrowed value does not live long enough
  |     |
  |     value captured here
3 | }
  |  -
  |  |
  |  `c` dropped here while still borrowed
  |  borrow later used here

I thought primitive types like char were copied by default; why do I need to explicitly move it (move || bar(c)) to get the code to compile?

Upvotes: 3

Views: 730

Answers (2)

Shepmaster
Shepmaster

Reputation: 431949

You are correct that the char is Copy, but what's tricky is exactly when the copy is performed.

Pretend that the Copy trait had a method called copy, similar to Clone::clone. As an example of what the compiler does, your closure becomes:

|| {
    let c = Copy::copy(&c);
    bar(c)
}

The closure only captures c by reference as that's all it needs to perform the copy when the closure is invoked. By returning the closure, you'd be trying to return the reference to the local, which is forbidden.

Using move forces the closure to capture c by value.

See also:

Upvotes: 6

Masklinn
Masklinn

Reputation: 42502

I thought primitive types like char were copied by default; why do I need to explicitly move it (move || bar(c)) to get the code to compile?

It's actually because char is Copy that the issue occurs: by default, Rust closures are "smart", so the way they capture things is as unrestrictive as possible.

This means if they can capture by reference they will capture by reference, if they can't but can capture by mut ref they'll do that, and only if they can't will they capture by value ("move").

Here, because char is Copy it can be captured by reference then copied into the callee. And so the closure does that, and you have to tell it to capture by value (using the move keyword) instead.

Upvotes: 4

Related Questions