Измените тип приемника канала с Receiver ‹T› на Receiver ‹U›

У меня есть интерфейс, который определяет метод, возвращающий получателя:

pub fn subscribe(to: &str) -> crossbeam_channel::Receiver<Message>;

Я использую библиотечный метод, который возвращает Receiver, но с другим типом сообщения:

pub fn subscribe(to: &str) -> crossbeam_channel::Receiver<lib::Message>;

Достаточно легко преобразовать lib::Message в Message, но как я могу реализовать интерфейс, который будет действовать как оболочка для этой библиотеки, чтобы возвращаемый тип был правильным?

Я попытался создать новый канал, но это не работает (я думаю), поскольку метод вернется, а затем больше не будет передавать сообщения на новый канал, поэтому receiver всегда будет пустым.

let sub_recv = subscription.receiver();
let (send, receiver) = crossbeam_channel::unbounded::<Message>();
for m in sub_recv.try_recv() {
    send.send(m.into()).map_err(|_| MQError::ConversionError)?;
}

Спасибо


person Kyle    schedule 14.04.2021    source источник
comment
Хм, почему бы не конвертировать в момент потребления значения?   -  person Netwave    schedule 14.04.2021
comment
Проблема в том, что crossbeam_channel::Receiver - это не интерфейс, а конкретный тип. Итак, да, если у вас есть реальный получатель, вам действительно нужно будет создать другую пару отправитель / получатель. Часть вашего кода отсутствует - это фоновый поток, который истощит исходный получатель, преобразует его и отправит отправителю. Также обратите внимание, что вы почти наверняка не хотите, чтобы промежуточный канал был неограниченным, потому что он не сможет обеспечить противодавление, если потребитель работает медленнее, чем производитель.   -  person user4815162342    schedule 14.04.2021
comment
@Netwave не понимаю, что вы имеете в виду. Если это интерфейс, и я потенциально хочу изменить реализацию этого интерфейса, чем мне придется менять потребителя? Зачем вообще использовать интерфейс в такой момент.   -  person Kyle    schedule 15.04.2021
comment
@ user4815162342 Да, фоновый поток был решением, но я подумал, что может быть более чистый способ сделать это, чем этот. Спасибо.   -  person Kyle    schedule 15.04.2021


Ответы (1)


Для преобразования между вашими типами вы должны запустить фоновую задачу, потому что в противном случае вы заблокируете поток, в котором вы пытаетесь это сделать.

Ссылка на игровую площадку

use crossbeam_channel::{Receiver, unbounded}; // 0.5.0

trait ReceiverCompatExt<T>
{
    fn convert(self) -> Receiver<T>;
}

impl<T, U> ReceiverCompatExt<U> for Receiver<T>
    where U: From<T>,
          T: Send + 'static,
          U: Send + 'static,
{
    fn convert(self) -> Receiver<U> {
        let (sender, receiver) = unbounded();
        
        std::thread::spawn(move || {
            while let Ok(value) = self.recv() {
                if sender.send(value.into()).is_err() {
                    break;
                }
            }
        });
        
        receiver
    }
}
person Inline    schedule 15.04.2021