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).
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)))
}
}
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`
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
.
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>,
}