This code compiles:
use std::thread;
struct Foo;
fn use_foo(foo: &Foo) {}
fn main() {
let foo = Foo {};
thread::spawn(move || {
use_foo(&foo);
});
}
but this code does not:
use std::thread;
struct Foo;
struct Bar<'a> {
foo: &'a Foo,
}
fn use_bar(bar: Bar) {}
fn main() {
let foo = Foo {};
let bar = Bar { foo: &foo };
thread::spawn(move || {
use_bar(bar);
});
}
Instead failing with
error[E0597]: `foo` does not live long enough
--> src/main.rs:15:26
|
15 | let bar = Bar { foo: &foo };
| ^^^^ borrowed value does not live long enough
16 | / thread::spawn(move || {
17 | | use_bar(bar);
18 | | });
| |______- argument requires that `foo` is borrowed for `'static`
19 | }
| - `foo` dropped here while still borrowed
This bears some similarity to this issue, however here there is only a single reference to foo. Is there any way to get around this indirection introduced by bar and move foo into the closure?
Playground
There are a few things that prevent the second example from working:
The compiler will not move variables into the closure if they aren't used by the closure. The fact that foo is used through bar is not a factor.
You cannot move a value that you were only given a shared reference to. In the general sense, other references may exist and would be very put off by even mutating the value.
Even if you were to move the value foo, any references derived from it would be invalidated. The compiler prevents this from happening. This means even if you did use both foo and bar in the closure it still wouldn't work. You'd need to recreate bar with a fresh reference to the moved foo.
So in summary, no, you cannot in general take a structure that contains references and convert it or wrap it up into an owned version so that you can send it to another thread.
You can perhaps avoid references by using indexes (if appropriate for your real use-case) or perhaps you could use shared ownership (by using Arc instead of references). Or of course simply delay creating your referencing structure until after it has been moved to the thread.
Related
There's a pattern I'm frequently encountering when writing async Rust code. Let's say I have a Foo struct (in real code, this could be something like a network client) and an async function that takes a reference to an instance of this struct:
struct Foo {
i: i32
}
async fn use_foo(foo: &Foo) -> () {
// do something with foo
}
Then, I want to run several use_foo concurrently, each with its own reference to a different Foo. A natural (but incorrect) way to do this would be:
use futures::stream::FuturesUnordered;
async fn foo_in_a_loop() {
let futures = FuturesUnordered::new();
for i in 0..10 {
let foo = Foo { i };
// does not compile: `foo` is dropped before the future is dropped
futures.push(use_foo(&foo));
}
}
which doesn't compile:
error[E0597]: `foo` does not live long enough
--> src/main.rs:53:30
|
53 | futures.push(use_foo(&foo));
| ^^^^ borrowed value does not live long enough
54 | }
| - `foo` dropped here while still borrowed
55 | }
| - borrow might be used here, when `futures` is dropped and runs the `Drop` code for type `FuturesUnordered`
|
= note: values in a scope are dropped in the opposite order they are defined
For more information about this error, try `rustc --explain E0597`
My question boils down to: what's the most idiomatic way to fix this error? I'd like to "force" use_foo to take ownership of the Foo instance.
Right now, I'm using a helper function that takes ownership of foo:
async fn use_foo_owned(foo: Foo) -> () {
use_foo(&foo).await
}
async fn foo_in_a_loop_owned() {
let futures = FuturesUnordered::new();
for i in 0..10 {
let foo = Foo { i };
futures.push(use_foo_owned(foo));
}
}
which does compile, but introducing an extra function just to please the borrow checker is clunky.
Note that sometimes use_foo may come from another crate and I may not be able to change its signature, but if there's an elegant solution that involves modifying use_foo, I'm interested too.
Use an async move {} block to create a future that takes ownership of foo and calls use_foo:
futures.push(async move { use_foo(&foo).await });
I'm holding a reference to a Struct inside another Struct, both of which are declared in the same block.
I later want to use the outer Struct in a closure which is run repeatedly and never returns.
The reference inside the Struct apparently does not live long enough, but in my understanding, it should never go out of scope, or at least should live at least as long as the Struct it is referring to:
struct MyStruct;
struct ReferenceStruct<'a> {
reference: &'a MyStruct
}
impl<'a> ReferenceStruct<'a> {
fn do_something(&self) -> () {}
}
fn run<F>(mut function: F) -> !
where
F: FnMut() -> () + 'static
{
loop {
function();
}
}
fn main() {
let my_struct = MyStruct;
let reference = ReferenceStruct { reference: &my_struct };
run(move || {
reference.do_something();
});
}
(link to playground)
The run function (for context) mirrors an event loop, similar to that of Winit, and in reality, I have another Struct which owns the value being referenced, but this example reproduces it in fewer lines.
The error:
error[E0597]: `my_struct` does not live long enough
--> src\main.rs:26:50
|
26 | let reference = ReferenceStruct { reference: &my_struct };
| ^^^^^^^^^^ borrowed value does not live long enough
27 |
28 | / run(move ||
29 | | {
30 | | reference.do_something();
31 | | });
| |______- argument requires that `my_struct` is borrowed for `'static`
32 | }
| - `my_struct` dropped here while still borrowed
It appears that my_struct is dropped at the end of main, but even if the program flow somehow escapes the loop, it surely lasts as long as the reference struct, which is as long as it needs to. I don't understand where or how this error could come about, or what to do about it.
Your problem is the lifetime bound 'static for the closure passed into run(). This means that the lifetime of reference is also 'static because it is moved into the closure, which in turn means that my_struct must also have static lifetime – and that is not the case.
Luckily, you do not need the 'static bound here. If you remove it, everything works:
...
fn run<F>(mut function: F) -> !
where
F: FnMut() -> ()
...
However, this might not be a solution in your use case if the event loop needs the closure to be 'static.
fn main() {
let mut a = String::from("dd");
let mut x = move || {
a.push_str("string: &str");
};
x();
x();
}
I have added move here to capture a but I am still able to call the x closure twice. Is a still borrowed as a mutable reference here? Why doesn't move force a move?
The variable a has indeed been moved into the closure:
fn main() {
let mut a = String::from("dd");
let mut x = move || {
a.push_str("string: &str");
};
x();
x();
a.len();
}
error[E0382]: borrow of moved value: `a`
--> src/main.rs:9:5
|
2 | let mut a = String::from("dd");
| ----- move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait
3 | let mut x = move || {
| ------- value moved into closure here
4 | a.push_str("string: &str");
| - variable moved due to use in closure
...
9 | a.len();
| ^ value borrowed here after move
It's unclear why you think that the closure x would become invalid after calling it, but it doesn't. No more than the same applied to a struct:
struct ClosureLike {
a: String,
}
impl ClosureLike {
fn call(&mut self) {
self.a.push_str("string: &str");
}
}
fn main() {
let a = String::from("dd");
let mut x = ClosureLike { a };
x.call();
x.call();
}
The question came from my wrong understanding of closures. The way it is documented in the Rust book also contributed to the confusion (I am not saying the book is bad). If anyone else had this same confusion, here is what I found.
Closures do not just store the scope and run it when its called. It captures the environment in the preferred way. The environment which contains a is stored in the closure. How the values are captured from the environment decides the trait.
The value of a persists until the closure exists, unless some operation moves it, such as if the closure returns a or a method consumes a. Here, nothing moves a out of the closure so the closure can be called as many times as I want.
A better understanding can be obtained from the FnOnce, FnMut, and Fn traits. These traits are decided by how the variables are captured by the closure, not by how the variables are moved into the closure. FnMut can be implemented on a closure where a value is moved .
This question already has answers here:
How can I swap in a new value for a field in a mutable reference to a structure?
(2 answers)
How do I move out of a struct field that is an Option?
(1 answer)
Closed 4 years ago.
I'm experimenting with Rust's Arc<Mutex>, trying to use it with a struct that is not in the default examples, but rather a custom one that is lazily initialized:
use std::sync::{Arc, Mutex};
#[macro_use]
extern crate lazy_static; // lazy_static = "1.2.0"
pub struct FooBar {}
lazy_static! {
static ref FOO_BAR: Arc<Mutex<Option<FooBar>>> = Arc::new(Mutex::new(None));
}
pub fn unpack_foo_bar() {
let foo_bar_arc = Arc::clone(&FOO_BAR);
let foo_bar_mutex_result = foo_bar_arc.lock();
let foo_bar_mutex = foo_bar_mutex_result.unwrap();
let foo_bar = foo_bar_mutex.unwrap();
// do something
}
The static FOO_BAR variable is later initialized by replacing the content of the option.
The code above won't compile:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:15:19
|
15 | let foo_bar = foo_bar_mutex.unwrap();
| ^^^^^^^^^^^^^ cannot move out of borrowed content
It would if FooBar were replaced with e. g. u32. The code also doesn't compile with String, but that type has built-in methods for cloning, which my type may not necessarily have.
Short of using foo_bar_mutex.as_ref().unwrap(), what other options do I have, particularly if I'd like to abstract the extraction of the FooBar instance into a method, like this:
pub fn unpack_foo_bar() -> Option<FooBar> {
let foo_bar_arc = Arc::clone(&FOO_BAR);
let foo_bar_mutex_result = foo_bar_arc.lock();
let foo_bar_mutex = foo_bar_mutex_result.unwrap();
let foo_bar_option = *foo_bar_mutex;
foo_bar_option
}
In this case, the compiler throws almost the same error, which is
error[E0507]: cannot move out of borrowed content
--> src/main.rs:34:26
|
11 | let foo_bar_option = *foo_bar_mutex;
| ^^^^^^^^^^^^^^
| |
| cannot move out of borrowed content
| help: consider using a reference instead: `&*foo_bar_mutex`
I have a sense that this would be much simpler if FooBar were easily cloned, but in the real world scenario I'm basing this example on FooBar has a field that is an instance of a third party library object that does not derive the Clone trait.
Using Option::take would only make the contained variable usable once. I'm not trying to take ownership of the FooBar instance. I don't care if it's owned as long as I can call its methods. Returning a reference would be great, but when doing this, the compiler complains:
pub fn unpack_foo_bar() -> &Option<FooBar> {
let foo_bar_arc = Arc::clone(&FOO_BAR);
let foo_bar_mutex_result = foo_bar_arc.lock();
let foo_bar_mutex = foo_bar_mutex_result.unwrap();
let foo_bar_option = foo_bar_mutex.as_ref();
foo_bar_option
}
Compiler's response:
error[E0106]: missing lifetime specifier
--> src/main.rs:30:28
|
7 | pub fn unpack_foo_bar() -> &Option<FooBar> {
| ^ help: consider giving it a 'static lifetime: `&'static`
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
Experimenting with &*foo_bar_mutex and adding 'static to the return type each opens up subsequent cans of compiler errors.
I have also tried experimenting with to_owned() on the FooBar reference and stumbled upon the owning_ref crate, but alas, haven't figured out how to make returning a reference work.
Final Update
Given that it appears impossible to pass on the reference to the Option<FooBar> to an external function caller, I decided to avoid this problem altogether by allowing passing in the methods relying on the FooBar instance as a closure:
pub fn unpack_foo_bar(use_foo_bar: fn(&FooBar)) {
let foo_bar_arc = Arc::clone(&FOO_BAR);
let foo_bar_mutex_result = foo_bar_arc.lock();
let foo_bar_mutex = foo_bar_mutex_result.unwrap();
let foo_bar_reference = foo_bar_mutex.as_ref().unwrap();
// pass in the closure that needs the foo bar instance
use_foo_bar(foo_bar_reference);
}
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.
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.