我刚刚在类型声明中使用
for<'a>
发现了一个怪癖:https://www.rustexplorer.com/b/vwnvu9
error[E0582]: binding for associated type `Output` references lifetime `'b`, which does not appear in the trait input types
--> src/main.rs:14:29
|
14 | dyn for<'b> Fn(ArgT) -> Pin<Box<dyn Future<Output = Result<ResT, ()>> + Send + 'b>> + Sync;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0582`.
这真的很奇怪......至少可以这么说。
代码:
/*
[dependencies]
async-trait = { version = "0.1.68" }
tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread"] }
*/
use std::{future::Future, marker::PhantomData, pin::Pin, sync::Arc};
trait Trait: Send + Sync + PlayerTrait {}
impl<T: PlayerTrait> Trait for T {}
pub type Lambda<ArgT, ResT> =
dyn for<'b> Fn(ArgT) -> Pin<Box<dyn Future<Output = Result<ResT, ()>> + Send + 'b>> + Sync;
#[async_trait::async_trait]
trait PlayerTrait: Send + Sync {
async fn player_change_name<'a>(
&self,
input: &PlayerChangeNameInput,
// lambda: &(dyn for<'b> Fn(
// PlayerMusts<'b, 'a>,
// ) -> Pin<Box<dyn Future<Output = Result<Player, ()>> + Send + 'b>>
// + Sync),
lambda: &Lambda<PlayerMusts, Player>,
) -> Result<Player, ()>;
}
struct PlayerChangeNameInput {
pub name: String,
}
struct Player {
pub id: String,
pub name: String,
}
struct PlayerMusts<'a, 'b: 'a> {
_lifetime: PhantomData<&'b ()>,
pub actual: &'a Player,
// pub other: &'a str,
}
#[async_trait::async_trait]
impl PlayerTrait for Repo {
async fn player_change_name<'a>(
&self,
input: &PlayerChangeNameInput,
lambda: &(dyn for<'b> Fn(
PlayerMusts<'b, 'a>,
) -> Pin<Box<dyn Future<Output = Result<Player, ()>> + Send + 'b>>
+ Sync),
) -> Result<Player, ()> {
// I need input here
dbg!(&input.name);
// I need to await for many things in here
// Eg. for DB connection pool...
// let mut db_connection = pool.begin().await?;
// Here I can query the actual player, I'm faking it now...
let actual = Player {
id: "1".to_string(),
name: "Bob".to_string(),
};
let worked_player = lambda(PlayerMusts {
_lifetime: PhantomData,
actual: &actual,
// other: "Other",
})
.await?;
// I'm saving the lambda result here, I'm faking it now...
// let result = db_connection.save(&worked_player, &actual).await?;
let result = worked_player;
// db_connection.save_and_close().await?;
Ok(result)
}
}
struct HandlerImpl {
repo: Arc<Repo>,
}
fn new_handler(repo: Arc<Repo>) -> Box<dyn Handler> {
Box::new(HandlerImpl { repo })
}
#[async_trait::async_trait]
trait Handler: Send + Sync {
async fn handle(&self, input: &PlayerChangeNameInput, whoami: &str) -> Result<Player, ()>;
}
#[async_trait::async_trait]
impl Handler for HandlerImpl {
async fn handle(&self, input: &PlayerChangeNameInput, whoami: &str) -> Result<Player, ()> {
// use whoami here...
dbg!(whoami);
let result = self
.repo
.player_change_name(input, &|musts| {
Box::pin(async {
let o = Player {
id: musts.actual.id.clone(),
name: input.name.to_string(),
};
Ok(o)
})
})
.await?;
Ok(result)
}
}
pub struct Repo {
// pub pool: Arc<DbPool>,
}
impl Repo {
pub fn new() -> Self {
Self {}
}
}
#[tokio::main]
async fn main() -> Result<(), ()> {
let db_repo = Arc::new(Repo::new());
let handler = new_handler(db_repo);
let whoami = "Myself";
let fake_input = &PlayerChangeNameInput {
name: "Frank".to_string(),
};
let result = handler.handle(fake_input, whoami).await?;
dbg!(result.name);
Ok(())
}