Service Registration

axum handlers execute in an asynchronous context. This requires that an injected service must be thread-safe. axum imposes that an such service must implement Send and Sync. Most structures will already satisfy this requirement and is generated by the compiler. If it doesn't, then the struct will have to be wrapped by another struct that satisfies this requirement. A trait, on the other hand, has several options and depends on the trait definition itself. The method you chose to use is largely based on your preference.

Thread-Safe Trait

A trait declares it is thread-safe if it requires implementing Send and Sync.

trait Service: Send + Sync {}

#[injectable(Service)]
struct ServiceImpl;

impl Service for ServiceImpl {}

async fn handler(Inject(service): Inject<dyn Service>) {}

Multiple Trait Implementation

If the original trait does not declare thread safety with Send and Sync, then a struct implementation can directly specify that it is thread-safe.

trait Service {}

#[injectable(Service + Send + Sync)]
struct ServiceImpl;

impl Service for ServiceImpl {}

async fn handler(Inject(service): Inject<dyn Service + Send + Sync>) {}

Trait Unification

If the original trait does not declare thread safety with Send and Sync, another alterative is to unify the trait with Send and Sync in a new trait. You might chose this approach for better usage ergonomics.

trait Service {}

trait ServiceAsync: Service + Send + Sync {}

#[injectable(ServiceAsync)]
struct ServiceImpl;

impl Service for ServiceImpl {}
impl ServiceAsync for ServiceImpl {}

async fn handler(Inject(service): Inject<dyn ServiceAsync>) {}