【问题标题】:Confused about variable lifetime in tokio::spawn(async move对 tokio::spawn(async move) 中的变量生命周期感到困惑
【发布时间】:2020-06-26 13:16:09
【问题描述】:

我是 rust 和 tokio async 的新手,我正在尝试编译以下看似简单的代码:

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?
}

但是编译器抱怨:

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 {
   |     ^^^^^^^^^^^^^^^^^

从我在该主题上找到的先前讨论和示例中,我了解到将对 network_config 的引用传递给 spawn-ed 闭包会导致生命周期问题,因为单独的线程可能会超过 network_config。 这就是为什么我将 network_config 的克隆移动到生成的线程,但似乎仍然存在终生歧义。

是否有任何额外的提示我可以给编译器以便它正确地获得生命周期?还是我做错了?

注意:NetworkConfig 类定义为:

#[derive(Debug, Deserialize)]
pub struct NetworkConfig {
    pub bind: String,
    pub node_key_file: String,
}

【问题讨论】:

  • 另请注意,实现编译器给出的帮助提示(将 tokio::spawn 替换为 tokio::spawn + '_)会导致语法错误。

标签: asynchronous rust lifetime spawn tokio


【解决方案1】:

如果您想克隆 NetworkConfig 值,请为其声明 Clone 特征:

#[derive(Debug, Clone)]
pub struct NetworkConfig {
    pub bind: String,
    pub node_key_file: String,
}

否则,对于接收器方法查找的规则,您最终将通过引用在引用上调用 Clone 以下Clone implementer

impl<'_, T> Clone for &'_ T

并且克隆的引用将具有绑定到clone() 调用范围的生命周期。

使用derive(Clone)run 函数可以编译,但它仅在network_config 参数具有'static 时才有效 生命周期,因为 tokio::spawn 生命周期要求。

这可能不是你想要的。如果是这种情况,则按值传递 NetworkConfig 并最终将其克隆到调用者中 语境。

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()));
}

您可能会问为什么这样有效,实际上仍然将引用传递给network_handler()

这是因为 network_config 被移动到 spawn 异步块内,并且 这使得异步块的推断类型获得静态生命周期。

【讨论】:

  • 这个答案值得一看:它不仅解决了问题,而且比大多数关于该主题的教程更深入地了解异步/生命周期。向你致敬@attdona!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-11
  • 2017-07-13
相关资源
最近更新 更多