简体   繁体   中英

Hiding associated type shared by two traits

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, ie 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,

  • I'm having difficulty actually creating a SendAny from a Send , and same for the receiver.
  • I hope there is a more elegant and efficient way, since this introduces boilerplate and needless boxing/unboxing.

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.

I don't quite know if this is what you are looking for, but you could make the SendAndReceive generic on the sender and receiver:

pub trait Sends {
    type Message;
    fn send(&self) -> Self::Message;
}

pub trait Receives {
    type Message;
    fn receive(&mut self, msg: Self::Message);
}

struct SendAndReceive<R,S> 
    where S: Sends, R: Receives<Message=S::Message>
{
    // These have to have matching Message types
    tx: S,
    rx: R,
}

impl<R,S> SendAndReceive<R,S> 
    where S: Sends, R: Receives<Message=S::Message>
{
    fn new(tx: S,rx: R,) -> Self {
        Self{tx, rx}
    }

    fn run(&mut self) {
        self.rx.receive(self.tx.send());
    }
}

The struct is therefore agnostic to the type of the message but generic on the sender/receiver. Also avoid the whole Any machinery.

You could also go the other way and make the struct generic on the message:

pub trait Sends {
    type Message;
    fn send(&self) -> Self::Message;
}

pub trait Receives {
    type Message;
    fn receive(&mut self, msg: Self::Message);
}

struct SendAndReceive<M> {
    // These have to have matching Message types
    tx: Box<dyn Sends<Message=M>>,
    rx: Box<dyn Receives<Message=M>>,
}

impl<M> SendAndReceive<M> {
    fn new(
        tx: Box<dyn Sends<Message = M>>,
        rx: Box<dyn Receives<Message = M>>,
    ) -> Self {
        Self{tx,rx}
    }

    fn run(&mut self) {
        self.rx.receive(self.tx.send());
    }
}

This again avoids Any and doesn't need to be generic on the sender/receiver but must be generic on the message type.

I don't know whether these two are what you are looking for, but I don't see any other way to avoid run needing specific types/traits.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM