Use `async fn (&mut self)` in `Send + !Sync` struct

I'm trying to make an abstract http client where one wraps am axum::Router.
I can't use a &mut self in the Client::get method as axum::Router implements Send and !Sync .
Does anyone have a way that I can use &mut self in an async fn when the struct is Send + !Sync?

#[derive(Debug)]
pub struct RouterClient {
    router: Arc<Router>,
}

#[async_trait]
pub trait Client {
    async fn get(&mut self);
}

#[async_trait]
impl Client for RouterClient {
    async fn get(&mut self) {
        todo!();
    }
}
error: future cannot be sent between threads safely
   --> http_server/tests/http_server/main.rs:208:29
    |
208 |       async fn get(&mut self) {
    |  _____________________________^
209 | |         todo!();
210 | |     }
    | |_____^ future created by async block is not `Send`
    |
    = help: the trait `Sync` is not implemented for `(dyn tower::util::boxed_clone::CloneService<Request<Body>, Error = Infallible, Response = Response<http_body::combinators::box_body::UnsyncBoxBody<axum::body::Bytes, axum::Error>>, Future = Pin<Box<(dyn std::future::Future<Output = Result<Response<http_body::combinators::box_body::UnsyncBoxBody<axum::body::Bytes, axum::Error>>, Infallible>> + Send + 'static)>>> + Send + 'static)`
note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
   --> http_server/tests/http_server/main.rs:208:23
    |
208 |     async fn get(&mut self) {
    |                       ^^^^ has type `&mut RouterClient` which is not `Send`, because `RouterClient` is not `Sync`
    = note: required for the cast to the object type `dyn std::future::Future<Output = ()> + Send`

There isn't any problem with async fn(&mut self) if your struct is Send + !Sync. However, your RouterClient struct is !Send + !Sync because you are wrapping the axum::Router in an Arc.

Why do you need to wrap it in an Arc?

The code I posted is a stripped down version to highlight the &mut self error. The real implementation binds together other domain types from the project into a specialized Client.

The Client used in production uses reqwest::Client to send a HTTP request, while the 'mock' Client used for testing, avoids communicating with a server process through a HTTP request in favor of constructing a hyper::Request and giving that directly to the axum::Router.

In short many Clients are made for some test cases and they currently share the same Router. Hence the use of Arc<Router>.

The simplest solution may be to clone the Router per client, this is a test Client after all. Another option would be to use a Mutex to guard the Router?

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.