Rust 中线程发布者的模拟订阅者

问题描述 投票:0回答:1

我正在实现命令模式的实现,其中订阅者将从读取器线程接收数据,并且我在使用 Rust 模拟测试我的实现时遇到问题。考虑这个精简的例子:

use std::thread;

pub trait Subscriber: Sync + Send {
    fn callback(&self);
}

pub struct Publisher {
    thread_handler: Option<thread::JoinHandle<()>>,
}

impl Publisher {
    pub fn new() -> Self {
        Self {
            thread_handler: None,
        }
    }

    pub fn start(&mut self, sub: Box<dyn Subscriber>) {
        self.thread_handler = Some(thread::spawn(move || {
            sub.callback();
        }));
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use mockall::*;

    mock! {
        TestSubscriber {}

        impl Subscriber for TestSubscriber {
            fn callback(&self);
        }
    }

    #[test]
    fn test_subscriber() {
        let mut publisher = Publisher::new();

        let mut mock_test_subscriber = Box::new(MockTestSubscriber::new());

        publisher.start(mock_test_subscriber);

        mock_test_subscriber.expect_callback().times(1);

        thread::sleep(std::time::Duration::from_millis(10));
    }
}

这会抛出

error[E0382]: borrow of moved value: `mock_test_subscriber`
  --> src/foo.rs:46:9
   |
42 |         let mut mock_test_subscriber = Box::new(MockTestSubscriber::new());
   |             ------------------------ move occurs because `mock_test_subscriber` has type `Box<MockTestSubscriber>`, which does not implement the `Copy` trait
43 |
44 |         publisher.start(mock_test_subscriber);
   |                           -------------------- value moved here
45 |
46 |         mock_test_subscriber.expect_callback().times(1);
   |         ^^^^^^^^^^^^^^^^^^^^ value borrowed here after move

我真的不知道如何解决这个问题,我尝试将

dyn Subscriber
包裹在
Arc
周围,但仍然无法使其工作。任何建议将不胜感激。或者也许有更好的设计来达到相同的结果。

multithreading testing rust design-patterns mocking
1个回答
0
投票

您尝试使用

Arc
的方向是正确的 - 它应该是在进行临时多线程时首先尝试的事情。由于您正在尝试在两个线程之间共享
mock_test_subscriber
,因此这正是您所需要的。所以首先我们将
Publisher::start
的签名更改为使用
Arc
:

pub fn start(&mut self, sub: Arc<dyn Subscriber>) {
    self.thread_handler = Some(thread::spawn(move || {
        sub.callback();
    }));
}

...并相应更新调用站点:

let mock_test_subscriber = Arc::new(MockTestSubscriber::new());
// create another reference by cloning the Arc
// then coerce the underlying concrete type into a trait object
publisher.start(Arc::clone(&mock_test_subscriber) as Arc<dyn Subscriber>);

请注意,我删除了

mut
上的
mock_test_subscriber
,因为这可能不是您的意思。写入
let mut foo = Arc::new<()>;
仅允许您将
foo
设置为另一个
Arc
(即指向其他内容),而不修改其基础值。
Arc
在语义上是共享引用,因此由于 Rust 关于可变别名的规则,它不能是可变的。从语法上讲,这表示为
Arc
未实现
DerefMut

要获取对基础值的可变引用,请使用

Arc<Mutex<T>>
Arc<RwLock<T>>

© www.soinside.com 2019 - 2024. All rights reserved.