Confused about variable lifetime in tokio::spawn(async move

匆匆过客 提交于 2020-12-06 04:34:25

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!