Given:
pub struct Needed {
pub repository_commands: Arc<dyn Trait>,
// and similar others...
}
and Trait
:
pub trait Trait: Send + Sync + Player + Shirt {}
impl<T: Player + Shirt> Trait for T {}
#[async_trait::async_trait]
pub trait PlayerCreateTrait {
//...
}
#[async_trait::async_trait]
pub trait Player: Send + Sync {
type PlayerCreate<'input>: Send + PlayerCreateTrait;
async fn player_create_start<'input>(
&self,
input: &'input PlayerInput,
) -> Result<Self::PlayerCreate<'input>, String>;
//...
}
#[async_trait::async_trait]
pub trait Shirt: Send + Sync {
//...
}
Right now I have 200+ Handler
structs all equals, one for each command of my application and all of them are like this:
pub struct Handler {
needs: Arc<Needed>,
}
Each of them has a different handle()
method (based on which command is). And each of them is in a separate file (mod).
This is for example for create_player
:
pub struct Handler {
needs: Arc<Needed>,
}
impl Handler {
pub async fn handle(&self, input: &PlayerCreateInput) -> Result<DomainPlayerCreated> {
// create_player
}
}
This is for update_player
:
pub struct Handler {
needs: Arc<Needed>,
}
impl Handler {
pub async fn handle(&self, input: &PlayerUpdateInput) -> Result<DomainPlayerUpdated> {
// update_player
}
}
This is for team_engage
:
pub struct Handler {
needs: Arc<Needed>,
}
impl Handler {
pub async fn handle(&self, team_id: String, player_ids: Vec<String>) -> Result<DomainTeamEngaged> {
// team_engage
}
}
and so on...
I asked for help (for another issue) and a great and kind person posted me an amazing example.
But in this example there are many news for me, one of them are the associated types
.
And I just found out that GATs
(generics associated types) are not liked by dyn
at all (Rust Explorer):
error[E0191]: the value of the associated type `PlayerCreate` (from trait `Player`) must be specified
--> src/main.rs:160:42
|
142 | type PlayerCreate<'input>: Send + PlayerCreateTrait;
| --------------------------------------------------- `PlayerCreate` defined here
...
160 | pub repository_commands: Arc<dyn Trait>,
| ^^^^^ help: specify the associated type: `Trait<PlayerCreate = Type>`
So the great user suggested me to use generics to work-around this problem, going from:
// in this example `Needed` struct is not used
pub struct Handler {
pub repository_commands: Arc<dyn Trait>,
}
impl Handler {
pub fn new(repository_commands: Arc<dyn Trait>) -> Self {
Self {
repository_commands,
}
}
pub async fn handle(&self, input: &PlayerInput) -> Result<DomainPlayer, String> {
// ...
}
}
to this (you can interact with the code here: Rust Explorer)
struct HandlerInner<C> {
repository_commands: C,
}
#[async_trait::async_trait]
trait ErasedHandler {
async fn handle(&self, input: &PlayerInput) -> Result<DomainPlayer, String>;
}
#[async_trait::async_trait]
impl<C: Send + Sync + Trait> ErasedHandler for HandlerInner<C> {
async fn handle(&self, input: &PlayerInput) -> Result<DomainPlayer, String> {
// ...
}
}
pub struct Handler(Arc<dyn ErasedHandler>);
impl Handler {
pub fn new<C: 'static + Trait>(repository_commands: C) -> Self {
Self(Arc::new(HandlerInner {
repository_commands,
}))
}
pub async fn handle(&self, input: &PlayerInput) -> Result<DomainPlayer, String> {
self.0.handle(input).await
}
}
but I cannot understand how to do this in my case: the complete and interactive code is on this playground here.
As you can see there are many mod
s there:
pub mod command {
pub mod player {
//...
}
pub mod shirt {
//...
}
pub mod team {
//...
}
}
is there a "simpler" way to avoid ErasedHandler
and all that?
Since I have 200+ equals handlers (one for each command) altough with different handle method, maybe using generics is a great solution, but how in this case?