问题
I am new to rust and tokio async, and I am trying to compile the following seemingly straightforward code:
async fn network_handler(network_config: &config::NetworkConfig) -> Result<(), Error> {
Ok(())
}
pub async fn run(network_config: &config::NetworkConfig) -> Result<(), Error> {
let network_config_copy = network_config.clone();
tokio::spawn(async move {
network_handler(&network_config_copy).await
}).await?
}
But the compiler complains:
error: cannot infer an appropriate lifetime
--> src/network.rs:43:18
|
43 | pub async fn run(network_config: &config::NetworkConfig) -> Result<(), Error> {
| ^^^^^^^^^^^^^^ ...but this borrow...
44 | let network_config_copy = network_config.clone();
45 | tokio::spawn(async move {
| ------------ this return type evaluates to the `'static` lifetime...
|
note: ...can't outlive the lifetime `'_` as defined on the function body at 43:34
--> src/network.rs:43:34
|
43 | pub async fn run(network_config: &config::NetworkConfig) -> Result<(), Error> {
| ^
help: you can add a constraint to the return type to make it last less than `'static` and match the lifetime `'_` as defined on the function body at 43:34
|
45 | tokio::spawn + '_(async move {
| ^^^^^^^^^^^^^^^^^
From the previous discussions and examples I have found on the subject, I understand that passing a reference to network_config to the spawn-ed closure would cause lifetime problems since the separate thread may outlive network_config. This is why I am moving a clone of network_config to the spawned thread, but there still seems to be a lifetime ambiguity.
Is there any extra hint I could give the compiler so that it correctly gets the lifetimes ? Or am I doing the whole thing wrong ?
Note: the NetworkConfig class is defined as:
#[derive(Debug, Deserialize)]
pub struct NetworkConfig {
pub bind: String,
pub node_key_file: String,
}
回答1:
If you want clone the NetworkConfig
value declare for it the Clone
trait:
#[derive(Debug, Clone)]
pub struct NetworkConfig {
pub bind: String,
pub node_key_file: String,
}
Otherwise, for the rules of receiver method lookup you will end up with invoking a Clone
on a reference through
the following Clone implementer:
impl<'_, T> Clone for &'_ T
And the cloned reference will have a lifetime bound to scope of clone()
invocation.
With derive(Clone)
the run
function compiles, but it works only when network_config
argument has 'static
lifetime, because of tokio::spawn lifetime requirement.
Probably this is not what you want. If this is the case pass NetworkConfig
by value and eventually clone it in the caller
context.
use async_std::io::Error;
use tokio;
mod config {
#[derive(Debug, Clone)]
pub struct NetworkConfig {
pub bind: String,
pub node_key_file: String,
}
}
async fn network_handler(network_config: &config::NetworkConfig) -> Result<(), Error> {
println!("using {:?}", network_config);
Ok(())
}
pub async fn run(network_config: config::NetworkConfig) -> Result<(), Error> {
tokio::spawn(async move { network_handler(&network_config).await }).await?
}
#[tokio::main]
async fn main() {
let config = config::NetworkConfig {
bind: "my_bind".to_owned(),
node_key_file: "abc".to_owned(),
};
tokio::spawn(run(config.clone()));
}
You may ask why this works, indeed a reference is still passed to network_handler()
.
This is because network_config
is moved inside the spawn async block and
this makes gaining static lifetime for the inferred type of the async block.
来源:https://stackoverflow.com/questions/60688816/confused-about-variable-lifetime-in-tokiospawnasync-move