Reputation: 51
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
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
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