Как сделать объект подписчика со свойствами RAII? - PullRequest
0 голосов
/ 03 февраля 2019

Я говорю с некоторыми аппаратными средствами по ссылке с типом модели издателя / подписчика.В C ++ я делал подписки с RAII, чтобы помнить, чтобы всегда отписываться, но я, кажется, не могу получить права собственности / заимствования прямо в ржавчине.

Наивно, это что-то вроде того, что я хотел бы сделать.send и receive, вероятно, должны быть &mut self, поэтому, как я понимаю, Subscription требуется изменяемый доступ к Transport.

struct Transport;

impl Transport {
    pub fn send(&mut self, cmd: &str) { unimplemented!() }
    pub fn subscribe(&mut self, cmd: &str) -> Subscription {
        self.send("subscribe-with-params");
        Subscription { trans: &mut self }
    }
}

struct Subscription {
    trans: &mut Transport,
}

impl Drop for Subscription {
    fn drop(&mut self) {
        self.trans.send("unsubscribe-with params");
    }
}

impl Subscription {
    fn receive(&mut self) -> &[u8] { /*blocking wait for data*/ }
}

fn test(t: Transport) {
    // Need to subscribe before command, as command might generate status messages
    let mut status_sub = t.subscribe("status-message");
    {
        let mut short_lived_sub = t.subscribe("command_reply");
        t.send("command");
        short_lived_sub.receive(); // Wait for ack
    }
    loop {
        println!("{:?}", status_sub.receive());
        /*processing of status */
    }
}

Здесь есть как минимум две проблемы.Во-первых, Subscription должен сохранять некоторую ссылку на его «родителя», Transport, а во-вторых, в fn test проблема заключается в том, что я не могу одолжить Transport дважды для двух разных подписок.

У меня такое чувство, что я здесь задаю не тот вопрос, так что, может быть, есть хороший способ полностью по-другому подойти к этому вопросу?

1 Ответ

0 голосов
/ 03 февраля 2019

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

Вместо этого вы можете использовать Rc (для общего владения) и RefCell (для внутренней изменчивости):

use std::rc::Rc;
use std::cell::RefCell;

struct TransportInner;

pub struct Transport {
    inner: Rc<RefCell<TransportInner>>,
}

pub struct Subscription { 
    trans: Rc<RefCell<TransportInner>>
}

impl TransportInner {
   pub fn send(&mut self, cmd: &str) { }
}

impl Transport {
   pub fn send(&mut self, cmd: &str) { 
       self.inner.borrow_mut().send(cmd)
   }

   pub fn subscribe(&mut self, cmd: &str) -> Subscription {
      self.send("subscribe-with-params");
      Subscription { trans: Rc::clone(&self.inner) }
   }
}

impl Drop for Subscription {
   fn drop(&mut self) {
      self.trans.borrow_mut().send("unsubscribe-with params");
   }
}

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

Если вам нужно, чтобы это работало между потоками, вы должны использовать Arc<Mutex>.

...