I'm trying to write a small webapp communicating with a client (aka Slave in this code) over gRPC (using Tonic) and I'm getting stuck almost at the very beginning. I want the slave to be able to self-register by invoking the register()
function over gRPC. The request at the moment contains only the mac_address (due to lack of any better unique device identifier I'm aware of).
The juicy part of the code looks as follows
pub struct SlaveServer {
slave_repo: Arc<SlaveRepository>
}
#[tonic::async_trait]
impl SlaveManager for SlaveServer {
async fn register(&self, request : Request<RegistrationRequest>) -> Result<Response<RegistrationResponse>, Status> {
let req: RegistrationRequest = request.into_inner();
(*self.slave_repo).add_slave(
Slave {
mac_address: req.mac_address.clone()
}
);
println!("New slave: {}", req.mac_address);
let response = RegistrationResponse { slave_id: "new_id".to_string() };
return Ok(Response::new(response))
}
}
unfortunately however it looks like I cannot invoke add_server
. It currently gives me the following error:
trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<SlaveRepository>`
It is my guess that an easy way to go around it would be to write register(&mut self,...)
, but that is not something I can even attempt, given that the SlaveManager
is auto-generated from a proto file and that one doesn't use the mut
.
I originally used Box<SlaveRepository>
, but that didn't seem to work either, plus I suppose it wasn't suitable since the SlaveRepository
will be used in several different places.
The Arc<T>
smart pointer gives you read-only access to its pointee. So you cannot modify something which is behind an Arc
, at least not directly.
Given that you are using Arc
which is a thread-safe reference counted pointer, I assume that this would be used across threads. In that case you should wrap your SlaveRepository
in some synchronization primitive, such as Mutex<T>
or RWLock<T>
(note that in async
context you should use the async
versions of those primitives, instead of the ones from std
):
pub struct SlaveServer {
slave_repo: Arc<RWLock<SlaveRepository>>
}
// skipped for brevity
let mut lock_guard = self.slave_repo
.write()
.await;
lock_quard.add_slave(
Slave {
mac_address: req.mac_address.clone()
}
);
This is called the interior mutability pattern