Смущает время жизни переменной в tokio :: spawn (asyn c move - PullRequest
1 голос
/ 15 марта 2020

Я новичок в Rust и Tokio Asyn c, и я пытаюсь скомпилировать следующий, казалось бы, простой код:

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 на закрытие, вызываемое порождением, вызовет проблемы с продолжительностью жизни, поскольку отдельный поток может пережить network_config. Вот почему я перемещаю клон network_config в порожденный поток, но, похоже, все еще существует неоднозначность на протяжении всей жизни.

Есть ли какая-либо дополнительная подсказка, которую я мог бы дать компилятору, чтобы он правильно получил время жизни? Или я все делаю неправильно?

Примечание: класс NetworkConfig определяется как:

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

1 Ответ

1 голос
/ 15 марта 2020

Если вы хотите клонировать значение NetworkConfig, объявите для него черту Clone:

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

В противном случае, для правил поиска метода получателя вы в конечном итоге вызовете Clone на ссылка через следующий Clone реализатор :

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 asyn c и это делает получение времени жизни stati c для логического типа блока asyn c.

...