如何制作具有 RAII 属性的订阅者对象?
How to make a subscriber object with RAII properties?
我正在通过 link 与某种 publisher/subscriber 模型的硬件通信。在 C++ 中,我使用 RAII 进行了订阅,以记住始终取消订阅,但我似乎无法在 Rust 中获得正确的 ownerships/borrows。
天真地,这就是我想做的事情。 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
应该如何保留对 "parent"、Transport
的引用,另一个是 fn test
中的问题,我不能借用 Transport
两次对于两个不同的订阅。
我觉得我在这里问错了问题,所以也许有一种完全不同的方法来解决这个问题?
你的 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");
}
}
您可以在不将其拆分为内部结构和外部结构的情况下执行此操作,但这将需要用户也通过 Rc
访问 Transport
,这可能很笨拙。
如果您需要跨线程工作,您应该使用 Arc<Mutex>
。
我正在通过 link 与某种 publisher/subscriber 模型的硬件通信。在 C++ 中,我使用 RAII 进行了订阅,以记住始终取消订阅,但我似乎无法在 Rust 中获得正确的 ownerships/borrows。
天真地,这就是我想做的事情。 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
应该如何保留对 "parent"、Transport
的引用,另一个是 fn test
中的问题,我不能借用 Transport
两次对于两个不同的订阅。
我觉得我在这里问错了问题,所以也许有一种完全不同的方法来解决这个问题?
你的 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");
}
}
您可以在不将其拆分为内部结构和外部结构的情况下执行此操作,但这将需要用户也通过 Rc
访问 Transport
,这可能很笨拙。
如果您需要跨线程工作,您应该使用 Arc<Mutex>
。