Reputation: 278
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.
Upvotes: 4
Views: 902
Reputation: 59827
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 });
Upvotes: 9