How to implement the same trait in multiple files/mods avoiding "conflicting implementations of trait" error?

In Golang I can define an interface like this:

type DBRepo interface {
  PlayerByID(id uint64) (*domain.Player, error)
  TeamByID(id uint64) (*domain.Team, error)
  // many others

and I can implement them like this using different files:

// file: real_db.go
type RealDB struct {
  db *realDB
}

// file: player.go
func (r RealDB) PlayerByID(id uint64) (*domain.Player, error) {
  return r.db... // get from DB
}

// file: team.go
func (r RealDB) TeamByID(id uint64) (*domain.Team, error) {
  return r.db... // get from DB
}

// many others (files and methods)

I cannot undestand how to do the same in Rust:

#[async_trait::async_trait]
pub trait DBRepo: Send + Sync {
    async fn player_by_id(&self, id: i64) -> Result<()>;
    async fn team_by_id(&self, id: i64) -> Result<()>;
}

but if I write the below code in different files (and different mods too):

// file: player.rs
#[async_trait::async_trait]
impl DBRepo for Repo {
    async fn player_by_id(&self, id: i64) -> Result<()> {
        Ok(()) // get from DB
    }
}

// file: team.rs
#[async_trait::async_trait]
impl DBRepo for Repo {
    async fn team_by_id(&self, id: i64) -> Result<()> {
        Ok(()) // get from DB
    }
}

I get from the compiler:

error[E0119]: conflicting implementations of trait `DBRepo` for type `Repo`
--> src\team.rs:22:1
   |
22 | impl DBRepo for Repo {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Repo`
   |
  ::: src\player.rs:22:1
   |
22 | impl DBRepo for Repo {
   | ----------------------------------- first implementation here

For more information about this error, try `rustc --explain E0119`.

How can I fix this?

I need to use all the methods on trait DBRepo, I cannot split it in many traits.

How about this?

// file: player.rs
impl Repo {
    async fn player_by_id_impl(&self, id: i64) -> Result<()> {
        Ok(()) // get from DB
    }
}
// file: team.rs
impl Repo {
    async fn team_by_id_impl(&self, id: i64) -> Result<()> {
        Ok(()) // get from DB
    }
}
#[async_trait::async_trait]
impl DBRepo for Repo {
    async fn player_by_id(&self, id: i64) -> Result<()> {
        self.player_by_id_impl(id).await
    }
    async fn team_by_id(&self, id: i64) -> Result<()> {
        self.team_by_id_impl(id).await
    }
}

It there a runtime penalty doing this?

In the absolute worst case there's the runtime penalty of calling a function[1]. There's a good chance the call to the inherent method will be inlined here though, since literally nothing else is happening in the trait method and the types are all statically known.


  1. which is cheap unless you're in a very tight loop or something ↩ī¸Ž

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.