如何在这个异步lambda中使用这个参数?

  • 本文关键字:参数 异步 lambda rust
  • 更新时间 :
  • 英文 :


我正在尝试下面的代码,但我无法理解如何修复错误,你能帮助我吗?

RUST PLAYGROUND DEMO: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=977a9e7a5bb503e900818b8ff67daf7e

代码:

use std::{future::Future, pin::Pin, sync::Arc};
#[async_trait::async_trait]
pub trait PlayerTrait: Send + Sync {
    async fn player_update<'a>(
        &'a self,
        input: PlayerInput,
        lambda: &(dyn Fn(
            PlayerLambdaArgs,
        ) -> Pin<Box<dyn Future<Output = Result<Player, String>> + Send + 'a>>
              + Sync),
    ) -> Result<Player, String>;
}
#[derive(Debug)]
pub struct Player {
    pub name: String,
}
impl Player {
    pub fn new(name: &str, team_id: i64) -> Result<Player, String> {
        if team_id > 100 {
            return Err("Sorry, this team is closed".to_string());
        }
        Ok(Player {
            name: name.to_string(),
        })
    }
}
pub struct PlayerInput {
    pub name: String,
}
pub struct PlayerLambdaArgs {
    pub random_team_id: i64,
}
pub struct DBRepo {
    pub db: String,
}
impl DBRepo {
    pub fn new(db: String) -> Self {
        Self { db }
    }
}
#[async_trait::async_trait]
impl PlayerTrait for DBRepo {
    async fn player_update<'a>(
        &'a self,
        _input: PlayerInput,
        lambda: &(dyn Fn(
            PlayerLambdaArgs,
        ) -> Pin<Box<dyn Future<Output = Result<Player, String>> + Send + 'a>>
              + Sync),
    ) -> Result<Player, String> {
        // get from DB here
        let random_team_id = 123;
        let lambda_args = PlayerLambdaArgs { random_team_id };
        let res = lambda(lambda_args).await?;
        // do something with res here
        Ok(Player { name: res.name })
    }
}
#[tokio::main]
async fn main() {
    let db_repo = Arc::new(DBRepo::new(String::from("hello")));
    let input = PlayerInput {
        name: "Bob".to_string(),
    };
    let player = db_repo
        .player_update(input, &|args| {
            Box::pin(async {
                let player = Player::new(&input.name, args.random_team_id)?;
                Ok(player)
            })
        })
        .await;
    dbg!(player);
}

错误:

Compiling playground v0.0.1 (/playground)
error[E0373]: async block may outlive the current function, but it borrows `args.random_team_id`, which is owned by the current function
  --> src/main.rs:82:28
   |
82 |               Box::pin(async {
   |  ____________________________^
83 | |                 let player = Player::new(&input.name, args.random_team_id)?;
   | |                                                       ------------------- `args.random_team_id` is borrowed here
84 | |
85 | |                 Ok(player)
86 | |             })
   | |_____________^ may outlive borrowed value `args.random_team_id`
   |
note: async block is returned here
  --> src/main.rs:82:13
   |
82 | /             Box::pin(async {
83 | |                 let player = Player::new(&input.name, args.random_team_id)?;
84 | |
85 | |                 Ok(player)
86 | |             })
   | |______________^
help: to force the async block to take ownership of `args.random_team_id` (and any other referenced variables), use the `move` keyword
   |
82 |             Box::pin(async move {
   |                            ++++
For more information about this error, try `rustc --explain E0373`.
error: could not compile `playground` due to previous error

所以,几乎总是,异步闭包也必须是move。这也意味着它们所在的闭包也必须是move

首先要做的是添加move:

#[tokio::main]
async fn main() {
    let db_repo = Arc::new(DBRepo::new(String::from("hello")));
    let input = PlayerInput {
        name: "Bob".to_string(),
    };
    let player = db_repo
        .player_update(input, &(move |args| {
            Box::pin(async move {
                let player = Player::new(&input.name, args.random_team_id)?;
                Ok(player)
            })
        }))
        .await;
    dbg!(player);
}

我们仍然在input.name周围得到一些错误。这是因为我们试图将input作为参数移动到player_update,并作为闭包和异步块的捕获。

那么让我们为PlayerInput构造添加一个克隆:

#[tokio::main]
async fn main() {
    let db_repo = Arc::new(DBRepo::new(String::from("hello")));
    let name = "Bob".to_string();
    let input = PlayerInput {
        name: name.clone(),
    };
    let player = db_repo
        .player_update(input, &(move |args| {
            Box::pin(async move {
                let player = Player::new(&name, args.random_team_id)?;
                Ok(player)
            })
        }))
        .await;
    dbg!(player);
}

这会导致最后一个移动错误。我们可以通过在闭包外部而不是在闭包内部引用name来解决这个问题:

#[tokio::main]
async fn main() {
    let db_repo = Arc::new(DBRepo::new(String::from("hello")));
    let name = "Bob".to_string();
    let name = &name;
    let input = PlayerInput {
        name: name.clone(),
    };
    let player = db_repo
        .player_update(input, &(move |args| {
            Box::pin(async move {
                let player = Player::new(name, args.random_team_id)?;
                Ok(player)
            })
        }))
        .await;
    dbg!(player);
}

游乐场

最新更新