如何使具有RAII属性的订户对象?

时间:2019-02-03 16:06:28

标签: rust raii borrow-checker interior-mutability

我正在通过某种发布者/订阅者模型通过某个链接与某些硬件进行对话。在C ++中,我使用RAII进行订阅以记住总是取消订阅,但是我似乎无法完全摆脱所有权/借款。

天真的,这就像我想做的一样。 sendreceive可能需要为&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 :(得分:2)

您的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>