我正在尝试下面的代码,但我无法理解如何修复错误,你能帮助我吗?
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);
}
游乐场