为什么 tokio::spawn 即使使用 .clone() 也会抱怨生命周期?

dmp*_*p32 5 lifetime rust async-await rust-tokio

我试图编译以下看似简单的代码,但出现错误:

use std::io::Error;

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

async fn network_handler(network_config: &NetworkConfig) -> Result<(), Error> {
    Ok(())
}

async fn run(network_config: &NetworkConfig) -> Result<(), Error> {
    let network_config_copy = network_config.clone();
    tokio::spawn(async move {
        network_handler(&network_config_copy).await
    }).await?
}
Run Code Online (Sandbox Code Playgroud)
use std::io::Error;

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

async fn network_handler(network_config: &NetworkConfig) -> Result<(), Error> {
    Ok(())
}

async fn run(network_config: &NetworkConfig) -> Result<(), Error> {
    let network_config_copy = network_config.clone();
    tokio::spawn(async move {
        network_handler(&network_config_copy).await
    }).await?
}
Run Code Online (Sandbox Code Playgroud)

从之前关于该主题的讨论和示例中,我了解到传递对network_config生成的闭包的引用会导致生命周期问题,因为单独的线程可能会过期network_config。这就是为什么我将 的克隆移动network_config到生成的线程,但似乎仍然存在终生模糊性。

我是否可以向编译器提供任何额外的提示,以便它正确获取生命周期?还是我整件事都做错了?

att*_*ona 5

如果你想克隆NetworkConfig你需要实现该Clone特征:

#[derive(Debug, Clone)]
struct NetworkConfig {
    bind: String,
    node_key_file: String,
}
Run Code Online (Sandbox Code Playgroud)

否则,对于接收器方法查找的规则,您最终将Clone通过以下Clone实现者调用引用:

impl<'_, T> Clone for &'_ T
Run Code Online (Sandbox Code Playgroud)

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

使用derive(Clone)run函数进行编译,但 由于生命周期要求,它仅在network_config参数具有生命周期时才有效。'statictokio::spawn

也许这不是你想要的。如果是这种情况,NetworkConfig则按值传递并最终在调用者上下文中克隆它。

use std::io::Error;

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

async fn network_handler(network_config: &NetworkConfig) -> Result<(), Error> {
    println!("using {:?}", network_config);
    Ok(())
}

async fn run(network_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()));
}
Run Code Online (Sandbox Code Playgroud)

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

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