Crate async_trait - error future created by async block is not Send

I was experimenting with the async_trait crate and stumble upon the following error:

future created by async block is not Send

by the following code:

#[async_trait::async_trait]
trait DatabaseManagerGetConnection {
    async fn get_connection(&self) -> Option<()>;
}

#[async_trait::async_trait]
trait EntityRepositoryExecute {
    async fn execute (&self) -> Option<()>;
}

trait TableMapperToEntity {
    fn to_entity(&self) -> Option<()>;
}

struct DatabaseManager {
    sqlx_db_pool: sqlx::Pool<sqlx::Postgres>
}

#[async_trait::async_trait]
impl DatabaseManagerGetConnection for DatabaseManager {
    async fn get_connection(&self) -> Option<()> {
        Some(())
    }
}

struct EntityRepository <D: DatabaseManagerGetConnection>{
    database_manager: std::sync::Arc<D>
}

#[async_trait::async_trait]
// impl <D: DatabaseManagerGetConnection + Send + Sync> EntityRepositoryExecute for EntityRepository<D> {
impl <D: DatabaseManagerGetConnection> EntityRepositoryExecute for EntityRepository<D> {
    async fn execute(&self) -> Option<()>{
        self.database_manager.get_connection().await.unwrap();
        Some(())
    }
}

fn main() {
}

After a couple hours trying to understand the issue I tried to swap Line 32

impl <D: DatabaseManagerGetConnection> EntityRepositoryExecute for EntityRepository<D> {

with line Line 31:

impl <D: DatabaseManagerGetConnection + Send + Sync> EntityRepositoryExecute for EntityRepository<D> {

and the error went way. But I still confused on:

  • why I would need to add Send and Sync trait to the D: DatabaseManagerGetConnection?
    • My understanding was that async_trait::async_trait macro was already adding these "guarantees" (traits) to the DatabaseManagerGetConnection trait return itself.
  • As far as I am aware:
    • Arc implements Send + Sync
    • and sqlx::Pool<sqlx::Postgres> also implements Send + Sync

What am I missing?

no it's not.

the async_trait macro asserts the bounds for the return type, the implementor (you) are resposbile to provide an implementation that produces a result whcih satisfies the bounds.

yes, but D: DatabaseManagerGetConnection is a generic type parameter, the type checker can only sees the bounds, it doesn't care what concrete types can be used to instantiate the generic impl.

if you want all the concrete types of DatabaseManagerGetConnection to be Send + Sync, make them the super traits:

trait DatabaseManagerGetConnection: Send + Sync {
    //...
}

now the trait can only be implemented for types that are Send + Sync, and the bound D: DatabaseManagerGetConnection will always carry the implicit guarantee D: Send + Sync.

that is not corect
Arc is a generic type, and whether it is Send or Sync depends on its parameters.
Arc<T> is Send if and only if T is Send + Sync
Arc<T> is Sync if and only if T is Send + Sync
as specified by the doc.
so for example Arc<RefCell<u32>> is neither Send nor Sync.

therefore EntityRepository<D> is Send + Sync only if D is, which is why you need the bound