I have traits for senders and receivers of a specific message type.
pub trait Sends {
type Message;
fn send(&self) -> Self::Message;
}
pub trait Receives {
type Message;
fn receive(&mut self, msg: Self::Message);
}
I want to be able to store a compatible pair of sender and receiver in a struct with a run()
method that passes messages, i.e. receiver.receive(sender.send())
.
My intuition is that this run()
method should not require knowledge of the message type (because all occurrences of the message type are handled internally), so the struct and its method should not expose the message type. I think keeping track of the message types also becomes impractical when you have a bigger sender-receiver network.
What is the best way to do this? I tried it out with Any
, which mostly works. However,
SendAny
from a Send
, and same for the receiver.Here is what I've got so far:
trait SendsAny {
fn send_any(&self) -> Box<dyn Any>;
}
impl<T> SendsAny for T
where
T: Sends,
T::Message: 'static,
{
fn send_any(&self) -> Box<dyn Any> {
Box::new(self.send())
}
}
// Similar for ReceivesAny
struct SendAndReceive {
// These have to have matching Message types
tx: Box<dyn SendsAny>,
rx: Box<dyn ReceivesAny>,
}
impl SendAndReceive {
fn new<M: 'static>(
tx: Box<dyn Sends<Message = M>>,
rx: Box<dyn Receives<Message = M>>,
) -> Self {
// This doesn't work
let tx = tx as Box<dyn SendsAny>;
todo!()
}
fn run(&mut self) {
self.rx.receive_any(self.tx.send_any());
}
}
You should make the type that binds the Sends
and Receives
together, here SendAndReceiveInner
. And then use a trait object, Box<dyn SendAndReceiveAny>
to use it in the type-erased form in SendAndReceive
.
struct SendAndReceiveInner<R, S>
where
S: Sends,
R: Receives<Message = S::Message>,
{
tx: S,
rx: R,
}
trait SendAndReceiveAny {
fn run(&mut self);
}
impl<R, S> SendAndReceiveAny for SendAndReceiveInner<R, S>
where
S: Sends,
R: Receives<Message = S::Message>,
{
fn run(&mut self) {
self.rx.receive(self.tx.send());
}
}
struct SendAndReceive {
inner: Box<dyn SendAndReceiveAny>,
}
impl SendAndReceive {
fn new<R, S>(tx: S, rx: R) -> Self
where
S: Sends + 'static,
R: Receives<Message = S::Message> + 'static,
{
Self {
inner: Box::new(SendAndReceiveInner{ tx, rx }),
}
}
fn run(&mut self) {
self.inner.run();
}
}
This has a lot less boxing involved but still a bit of boilerplate. You could just use it in the Box<dyn ...>
form since the outermost SendAndReceive
isn't doing much at this point, but encapsulation and API presentation is up to the reader.