Reputation: 21698
This code does not compile without adding move
to the closure. It produces the error:
error[E0373]: closure may outlive the current function, but it borrows `foo`, which is owned by the current function
--> src/main.rs:26:18
|
26 | do_something(|| {
| ^^ may outlive borrowed value `foo`
27 | foo.bar += 1;
| --- `foo` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:26:5
|
26 | / do_something(|| {
27 | | foo.bar += 1;
28 | | println!("{}", foo.bar);
29 | | });
| |______^
help: to force the closure to take ownership of `foo` (and any other referenced variables), use the `move` keyword
|
26 | do_something(move || {
| ^^^^^^^
This error is confusing, as it seems to imply a lifetime longer than 'static is required, as if anything could outlive 'static. Here foo
is a 'static
reference to a struct. If I add move
to the closure, isn't it going to move the referenced struct itself into the closure? Or is it just copying the reference (the pointer bits)? It seems to me that it's just moving the reference, not Foo
itself. My understanding is very fuzzy about how move closures work with respect to references.
struct Foo {
bar: i32,
}
impl Drop for Foo {
fn drop(&mut self) {
panic!("dropping Foo, should never happen");
}
}
pub fn do_something<F, T>(mut f: F)
where
F: FnMut() -> T + 'static,
T: 'static,
{
// "spawn" 3 threads
f();
f();
f();
}
fn main() {
let foo = Box::leak::<'static>(Box::new(Foo { bar: 0 }));
let raw_ptr: *const Foo = foo;
do_something(move || {
foo.bar += 1;
println!("{}", foo.bar);
});
println!("{}", unsafe { (*raw_ptr).bar });
}
This question is similar to Specifying static lifetime of value in `main` so callback can borrow but I don't think it's a duplicate.
Upvotes: 2
Views: 1078
Reputation: 5390
move
causes the value of any captured variable to be moved, whilst if move
is not specified, and the closure does not consume the variable, the closure will try to take a reference to it instead. Although the variable is captured by reference, within the closure the name still acts as if it were the original variable.
The key point of confusion here comes from the fact foo
has type &mut Foo
, not Foo
, which are distinct types.
So in this case, without the move
a reference to foo
is created with type &'a mut &'static mut Foo
, where 'a
is the lifetime of foo
within the main
method. This causes an error as it restricts the lifetime of the closure to less than the 'static
required by do_something
.
Upvotes: 6