2

Summary

When trying to have a tonic server class depend on a trait, I get a compile-time issue about the trait not implementing Send or Sync. My intent is to have the server class depend on the trait rather than the concrete implementation of that trait (inversion of control).

Setup

I have a RegistrationServiceApi trait that is implemented by a RegistrationServiceImpl

pub trait RegistrationServiceApi {
    fn register_user(&self, request: RegisterUserRequest) -> RegisterUserResponse;
}

#[derive(Debug, Default)]
pub struct RegistrationServiceImpl {}

impl RegistrationServiceApi for RegistrationServiceImpl {
    fn register_user(&self, request: RegisterUserRequest) -> RegisterUserResponse {
        ...
    }
}

I also have a RegistrationServiceServerImpl that is implementing a RegistrationService gRPC class generated by prost (annotated with #[tonic::async_trait]) that depends on theRegistrationServiceApi.

pub struct RegistrationServiceServerImpl {
    registration_service: Arc<dyn RegistrationServiceApi>,
}

impl RegistrationServiceServerImpl {
    pub fn new(registration_service: Arc<RegistrationServiceImpl>) -> Self {
        RegistrationServiceServerImpl {
            registration_service,
        }
    }
}

#[tonic::async_trait]
impl RegistrationService for RegistrationServiceServerImpl {
    async fn register_user(
        &self,
        request: Request<RegisterUserRequest>,
    ) -> Result<Response<RegisterUserResponse>, Status> {
        let req = request.into_inner();
        Ok(Response::new(self.registration_service.register_user(req)))
    }
}

Problem

The setup above yields the following compiler error:

error[E0277]: `(dyn RegistrationServiceApi + 'static)` cannot be sent between threads safely
   --> project/src/registration_service_server.rs:21:6
    |
21  | impl RegistrationService for RegistrationServiceServerImpl {
    |      ^^^^^^^^^^^^^^^^^^^ `(dyn RegistrationServiceApi + 'static)` cannot be sent between threads safely
    |
    = help: the trait `Send` is not implemented for `(dyn RegistrationServiceApi + 'static)`
    = note: required because of the requirements on the impl of `Sync` for `Arc<(dyn RegistrationServiceApi + 'static)>`
note: required because it appears within the type `RegistrationServiceServerImpl`
   --> project/src/registration_service_server.rs:8:12
    |
8   | pub struct RegistrationServiceServerImpl {
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `RegistrationService`
   --> 
    |
194 |     pub trait RegistrationService: Send + Sync + 'static {
    |                                           ^^^^ required by this bound in `RegistrationService`

Attempts

I've tried replacing the dependency on the trait with a dependency on the implementation class:

pub struct RegistrationServiceServerImpl {
    registration_service: Arc<RegistrationServiceImpl>,
}

This works but violates inversion of control.

I've also tried having the RegistrationServiceApi extend Send and Sync, but that doesn't resolve the issue. In hindsight, that makes sense given the error was about dyn RegistrationServiceApi + 'static.

san4d
  • 71
  • 6
  • Making the trait having `Send` and `Sync` as supertraits should work. Can you show the code? – Chayim Friedman Sep 05 '22 at 13:42
  • You can also try changing `registration_service: Arc` to `registration_service: Arc`. – user4815162342 Sep 05 '22 at 13:43
  • Thanks for the feedback. I added both `Send` and `Sync` as super traits: ``` `(dyn RegistrationServiceApi + 'static)` doesn't implement `Debug` the trait `Debug` is not implemented for `(dyn RegistrationServiceApi + 'static)` the following other types implement trait `Debug`: ``` Now, I see an exception about the debug trait: `the trait `Debug` is not implemented for `(dyn RegistrationServiceApi + 'static)`` – san4d Sep 05 '22 at 13:47
  • How is `Debug` related here? – Chayim Friedman Sep 05 '22 at 13:54
  • It was required because the `RegistrationServiceServerImpl` had the derive debug macro. My issue is resolved by adding both send and sync as supertypes. Thanks for the help! – san4d Sep 05 '22 at 13:56

1 Answers1

2

The solution, thanks to Chayim Friedman was to add both Sync and Send as supertraits.

pub trait RegistrationServiceApi: Send + Sync {
    fn register_user(&self, request: RegisterUserRequest) -> RegisterUserResponse;
}

pub struct RegistrationServiceServerImpl {
    registration_service: Arc<dyn RegistrationServiceApi>,
}
san4d
  • 71
  • 6